xref: /freebsd/sys/dev/mfi/mfi.c (revision b670c9bafc0e31c7609969bf374b2e80bdc00211)
12e21a3efSScott Long /*-
2eebd9d53SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
42e21a3efSScott Long  * Copyright (c) 2006 IronPort Systems
52e21a3efSScott Long  * All rights reserved.
62e21a3efSScott Long  *
72e21a3efSScott Long  * Redistribution and use in source and binary forms, with or without
82e21a3efSScott Long  * modification, are permitted provided that the following conditions
92e21a3efSScott Long  * are met:
102e21a3efSScott Long  * 1. Redistributions of source code must retain the above copyright
112e21a3efSScott Long  *    notice, this list of conditions and the following disclaimer.
122e21a3efSScott Long  * 2. Redistributions in binary form must reproduce the above copyright
132e21a3efSScott Long  *    notice, this list of conditions and the following disclaimer in the
142e21a3efSScott Long  *    documentation and/or other materials provided with the distribution.
152e21a3efSScott Long  *
162e21a3efSScott Long  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
172e21a3efSScott Long  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
182e21a3efSScott Long  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
192e21a3efSScott Long  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
202e21a3efSScott Long  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
212e21a3efSScott Long  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
222e21a3efSScott Long  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
232e21a3efSScott Long  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
242e21a3efSScott Long  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
252e21a3efSScott Long  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
262e21a3efSScott Long  * SUCH DAMAGE.
272e21a3efSScott Long  */
28610f2ef3SScott Long /*-
29610f2ef3SScott Long  * Copyright (c) 2007 LSI Corp.
30610f2ef3SScott Long  * Copyright (c) 2007 Rajesh Prabhakaran.
31610f2ef3SScott Long  * All rights reserved.
32610f2ef3SScott Long  *
33610f2ef3SScott Long  * Redistribution and use in source and binary forms, with or without
34610f2ef3SScott Long  * modification, are permitted provided that the following conditions
35610f2ef3SScott Long  * are met:
36610f2ef3SScott Long  * 1. Redistributions of source code must retain the above copyright
37610f2ef3SScott Long  *    notice, this list of conditions and the following disclaimer.
38610f2ef3SScott Long  * 2. Redistributions in binary form must reproduce the above copyright
39610f2ef3SScott Long  *    notice, this list of conditions and the following disclaimer in the
40610f2ef3SScott Long  *    documentation and/or other materials provided with the distribution.
41610f2ef3SScott Long  *
42610f2ef3SScott Long  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
43610f2ef3SScott Long  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44610f2ef3SScott Long  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45610f2ef3SScott Long  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
46610f2ef3SScott Long  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47610f2ef3SScott Long  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
48610f2ef3SScott Long  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49610f2ef3SScott Long  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50610f2ef3SScott Long  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51610f2ef3SScott Long  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52610f2ef3SScott Long  * SUCH DAMAGE.
53610f2ef3SScott Long  */
542e21a3efSScott Long 
552e21a3efSScott Long #include <sys/cdefs.h>
562e21a3efSScott Long #include "opt_mfi.h"
572e21a3efSScott Long 
582e21a3efSScott Long #include <sys/param.h>
592e21a3efSScott Long #include <sys/systm.h>
6047b470b9SDoug Ambrisko #include <sys/sysctl.h>
612e21a3efSScott Long #include <sys/malloc.h>
622e21a3efSScott Long #include <sys/kernel.h>
63741367d5SDoug Ambrisko #include <sys/poll.h>
64741367d5SDoug Ambrisko #include <sys/selinfo.h>
652e21a3efSScott Long #include <sys/bus.h>
662e21a3efSScott Long #include <sys/conf.h>
672e21a3efSScott Long #include <sys/eventhandler.h>
682e21a3efSScott Long #include <sys/rman.h>
692e21a3efSScott Long #include <sys/bio.h>
702e21a3efSScott Long #include <sys/ioccom.h>
71741367d5SDoug Ambrisko #include <sys/uio.h>
72741367d5SDoug Ambrisko #include <sys/proc.h>
73441f6d5dSScott Long #include <sys/signalvar.h>
74da146236SJohn Baldwin #include <sys/sysent.h>
75a6ba0fd6SDoug Ambrisko #include <sys/taskqueue.h>
762e21a3efSScott Long 
772e21a3efSScott Long #include <machine/bus.h>
782e21a3efSScott Long #include <machine/resource.h>
792e21a3efSScott Long 
802e21a3efSScott Long #include <dev/mfi/mfireg.h>
812e21a3efSScott Long #include <dev/mfi/mfi_ioctl.h>
822e21a3efSScott Long #include <dev/mfi/mfivar.h>
830d9a4ef3SDoug Ambrisko #include <sys/interrupt.h>
840d9a4ef3SDoug Ambrisko #include <sys/priority.h>
852e21a3efSScott Long 
862e21a3efSScott Long static int	mfi_alloc_commands(struct mfi_softc *);
872e21a3efSScott Long static int	mfi_comms_init(struct mfi_softc *);
882e21a3efSScott Long static int	mfi_get_controller_info(struct mfi_softc *);
89741367d5SDoug Ambrisko static int	mfi_get_log_state(struct mfi_softc *,
90fb595e7aSPaul Saab 		    struct mfi_evt_log_state **);
91c1ed06a8SJohn Baldwin static int	mfi_parse_entries(struct mfi_softc *, int, int);
922e21a3efSScott Long static void	mfi_data_cb(void *, bus_dma_segment_t *, int, int);
932e21a3efSScott Long static void	mfi_startup(void *arg);
942e21a3efSScott Long static void	mfi_intr(void *arg);
95c0b332d1SPaul Saab static void	mfi_ldprobe(struct mfi_softc *sc);
960d9a4ef3SDoug Ambrisko static void	mfi_syspdprobe(struct mfi_softc *sc);
97a6ba0fd6SDoug Ambrisko static void	mfi_handle_evt(void *context, int pending);
98741367d5SDoug Ambrisko static int	mfi_aen_register(struct mfi_softc *sc, int seq, int locale);
99741367d5SDoug Ambrisko static void	mfi_aen_complete(struct mfi_command *);
100c0b332d1SPaul Saab static int	mfi_add_ld(struct mfi_softc *sc, int);
101c0b332d1SPaul Saab static void	mfi_add_ld_complete(struct mfi_command *);
1020d9a4ef3SDoug Ambrisko static int	mfi_add_sys_pd(struct mfi_softc *sc, int);
1030d9a4ef3SDoug Ambrisko static void	mfi_add_sys_pd_complete(struct mfi_command *);
1042e21a3efSScott Long static struct mfi_command * mfi_bio_command(struct mfi_softc *);
1052e21a3efSScott Long static void	mfi_bio_complete(struct mfi_command *);
1060d9a4ef3SDoug Ambrisko static struct mfi_command *mfi_build_ldio(struct mfi_softc *,struct bio*);
1070d9a4ef3SDoug Ambrisko static struct mfi_command *mfi_build_syspdio(struct mfi_softc *,struct bio*);
1082e21a3efSScott Long static int	mfi_send_frame(struct mfi_softc *, struct mfi_command *);
10908c89430SSteven Hartland static int	mfi_std_send_frame(struct mfi_softc *, struct mfi_command *);
11058ef3154SDoug Ambrisko static int	mfi_abort(struct mfi_softc *, struct mfi_command **);
11100b4e54aSWarner Losh static int	mfi_linux_ioctl_int(struct cdev *, u_long, caddr_t, int, struct thread *);
1125ba21ff1SScott Long static void	mfi_timeout(void *);
1130929b669SScott Long static int	mfi_user_command(struct mfi_softc *,
1140929b669SScott Long 		    struct mfi_ioc_passthru *);
115610f2ef3SScott Long static void	mfi_enable_intr_xscale(struct mfi_softc *sc);
116610f2ef3SScott Long static void	mfi_enable_intr_ppc(struct mfi_softc *sc);
117610f2ef3SScott Long static int32_t	mfi_read_fw_status_xscale(struct mfi_softc *sc);
118610f2ef3SScott Long static int32_t	mfi_read_fw_status_ppc(struct mfi_softc *sc);
119610f2ef3SScott Long static int	mfi_check_clear_intr_xscale(struct mfi_softc *sc);
120610f2ef3SScott Long static int	mfi_check_clear_intr_ppc(struct mfi_softc *sc);
121a6ba0fd6SDoug Ambrisko static void 	mfi_issue_cmd_xscale(struct mfi_softc *sc, bus_addr_t bus_add,
122a6ba0fd6SDoug Ambrisko 		    uint32_t frame_cnt);
123a6ba0fd6SDoug Ambrisko static void 	mfi_issue_cmd_ppc(struct mfi_softc *sc, bus_addr_t bus_add,
124a6ba0fd6SDoug Ambrisko 		    uint32_t frame_cnt);
1250d9a4ef3SDoug Ambrisko static int mfi_config_lock(struct mfi_softc *sc, uint32_t opcode);
1260d9a4ef3SDoug Ambrisko static void mfi_config_unlock(struct mfi_softc *sc, int locked);
1270d9a4ef3SDoug Ambrisko static int mfi_check_command_pre(struct mfi_softc *sc, struct mfi_command *cm);
1280d9a4ef3SDoug Ambrisko static void mfi_check_command_post(struct mfi_softc *sc, struct mfi_command *cm);
1290d9a4ef3SDoug Ambrisko static int mfi_check_for_sscd(struct mfi_softc *sc, struct mfi_command *cm);
13047b470b9SDoug Ambrisko 
1317029da5cSPawel Biernacki SYSCTL_NODE(_hw, OID_AUTO, mfi, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
1327029da5cSPawel Biernacki     "MFI driver parameters");
13347b470b9SDoug Ambrisko static int	mfi_event_locale = MFI_EVT_LOCALE_ALL;
13408c89430SSteven Hartland SYSCTL_INT(_hw_mfi, OID_AUTO, event_locale, CTLFLAG_RWTUN, &mfi_event_locale,
13547b470b9SDoug Ambrisko            0, "event message locale");
136a1adc445SDoug Ambrisko 
137c953d939SScott Long static int	mfi_event_class = MFI_EVT_CLASS_INFO;
13808c89430SSteven Hartland SYSCTL_INT(_hw_mfi, OID_AUTO, event_class, CTLFLAG_RWTUN, &mfi_event_class,
13947b470b9SDoug Ambrisko            0, "event message class");
14047b470b9SDoug Ambrisko 
1410929b669SScott Long static int	mfi_max_cmds = 128;
14208c89430SSteven Hartland SYSCTL_INT(_hw_mfi, OID_AUTO, max_cmds, CTLFLAG_RDTUN, &mfi_max_cmds,
14308c89430SSteven Hartland 	   0, "Max commands limit (-1 = controller limit)");
1440929b669SScott Long 
145a6ba0fd6SDoug Ambrisko static int	mfi_detect_jbod_change = 1;
14608c89430SSteven Hartland SYSCTL_INT(_hw_mfi, OID_AUTO, detect_jbod_change, CTLFLAG_RWTUN,
147a6ba0fd6SDoug Ambrisko 	   &mfi_detect_jbod_change, 0, "Detect a change to a JBOD");
148a6ba0fd6SDoug Ambrisko 
14908c89430SSteven Hartland int		mfi_polled_cmd_timeout = MFI_POLL_TIMEOUT_SECS;
15008c89430SSteven Hartland SYSCTL_INT(_hw_mfi, OID_AUTO, polled_cmd_timeout, CTLFLAG_RWTUN,
15108c89430SSteven Hartland 	   &mfi_polled_cmd_timeout, 0,
15208c89430SSteven Hartland 	   "Polled command timeout - used for firmware flash etc (in seconds)");
15308c89430SSteven Hartland 
154f108bdaaSSteven Hartland static int	mfi_cmd_timeout = MFI_CMD_TIMEOUT;
155f108bdaaSSteven Hartland SYSCTL_INT(_hw_mfi, OID_AUTO, cmd_timeout, CTLFLAG_RWTUN, &mfi_cmd_timeout,
156f108bdaaSSteven Hartland 	   0, "Command timeout (in seconds)");
157f108bdaaSSteven Hartland 
1582e21a3efSScott Long /* Management interface */
1592e21a3efSScott Long static d_open_t		mfi_open;
1602e21a3efSScott Long static d_close_t	mfi_close;
1612e21a3efSScott Long static d_ioctl_t	mfi_ioctl;
162741367d5SDoug Ambrisko static d_poll_t		mfi_poll;
1632e21a3efSScott Long 
1642e21a3efSScott Long static struct cdevsw mfi_cdevsw = {
1652e21a3efSScott Long 	.d_version = 	D_VERSION,
1662e21a3efSScott Long 	.d_flags =	0,
1672e21a3efSScott Long 	.d_open = 	mfi_open,
1682e21a3efSScott Long 	.d_close =	mfi_close,
1692e21a3efSScott Long 	.d_ioctl =	mfi_ioctl,
170741367d5SDoug Ambrisko 	.d_poll =	mfi_poll,
1712e21a3efSScott Long 	.d_name =	"mfi",
1722e21a3efSScott Long };
1732e21a3efSScott Long 
1742e21a3efSScott Long MALLOC_DEFINE(M_MFIBUF, "mfibuf", "Buffers for the MFI driver");
1752e21a3efSScott Long 
1762e21a3efSScott Long #define MFI_INQ_LENGTH SHORT_INQUIRY_LENGTH
1770d9a4ef3SDoug Ambrisko struct mfi_skinny_dma_info mfi_skinny;
1782e21a3efSScott Long 
179610f2ef3SScott Long static void
mfi_enable_intr_xscale(struct mfi_softc * sc)180610f2ef3SScott Long mfi_enable_intr_xscale(struct mfi_softc *sc)
181610f2ef3SScott Long {
182610f2ef3SScott Long 	MFI_WRITE4(sc, MFI_OMSK, 0x01);
183610f2ef3SScott Long }
184610f2ef3SScott Long 
185610f2ef3SScott Long static void
mfi_enable_intr_ppc(struct mfi_softc * sc)186610f2ef3SScott Long mfi_enable_intr_ppc(struct mfi_softc *sc)
187610f2ef3SScott Long {
188fa1e6ef4SDoug Ambrisko 	if (sc->mfi_flags & MFI_FLAGS_1078) {
1890d9a4ef3SDoug Ambrisko 		MFI_WRITE4(sc, MFI_ODCR0, 0xFFFFFFFF);
190610f2ef3SScott Long 		MFI_WRITE4(sc, MFI_OMSK, ~MFI_1078_EIM);
1910d9a4ef3SDoug Ambrisko 	}
1920d9a4ef3SDoug Ambrisko 	else if (sc->mfi_flags & MFI_FLAGS_GEN2) {
1930d9a4ef3SDoug Ambrisko 		MFI_WRITE4(sc, MFI_ODCR0, 0xFFFFFFFF);
194fa1e6ef4SDoug Ambrisko 		MFI_WRITE4(sc, MFI_OMSK, ~MFI_GEN2_EIM);
195fa1e6ef4SDoug Ambrisko 	}
1960d9a4ef3SDoug Ambrisko 	else if (sc->mfi_flags & MFI_FLAGS_SKINNY) {
1970d9a4ef3SDoug Ambrisko 		MFI_WRITE4(sc, MFI_OMSK, ~0x00000001);
1980d9a4ef3SDoug Ambrisko 	}
199610f2ef3SScott Long }
200610f2ef3SScott Long 
201610f2ef3SScott Long static int32_t
mfi_read_fw_status_xscale(struct mfi_softc * sc)202610f2ef3SScott Long mfi_read_fw_status_xscale(struct mfi_softc *sc)
203610f2ef3SScott Long {
204610f2ef3SScott Long 	return MFI_READ4(sc, MFI_OMSG0);
205610f2ef3SScott Long }
206610f2ef3SScott Long 
207610f2ef3SScott Long static int32_t
mfi_read_fw_status_ppc(struct mfi_softc * sc)208610f2ef3SScott Long mfi_read_fw_status_ppc(struct mfi_softc *sc)
209610f2ef3SScott Long {
210610f2ef3SScott Long 	return MFI_READ4(sc, MFI_OSP0);
211610f2ef3SScott Long }
212610f2ef3SScott Long 
213610f2ef3SScott Long static int
mfi_check_clear_intr_xscale(struct mfi_softc * sc)214610f2ef3SScott Long mfi_check_clear_intr_xscale(struct mfi_softc *sc)
215610f2ef3SScott Long {
216610f2ef3SScott Long 	int32_t status;
217610f2ef3SScott Long 
218610f2ef3SScott Long 	status = MFI_READ4(sc, MFI_OSTS);
219610f2ef3SScott Long 	if ((status & MFI_OSTS_INTR_VALID) == 0)
220610f2ef3SScott Long 		return 1;
221610f2ef3SScott Long 
222610f2ef3SScott Long 	MFI_WRITE4(sc, MFI_OSTS, status);
223610f2ef3SScott Long 	return 0;
224610f2ef3SScott Long }
225610f2ef3SScott Long 
226610f2ef3SScott Long static int
mfi_check_clear_intr_ppc(struct mfi_softc * sc)227610f2ef3SScott Long mfi_check_clear_intr_ppc(struct mfi_softc *sc)
228610f2ef3SScott Long {
229610f2ef3SScott Long 	int32_t status;
230610f2ef3SScott Long 
231610f2ef3SScott Long 	status = MFI_READ4(sc, MFI_OSTS);
232fa1e6ef4SDoug Ambrisko 	if (sc->mfi_flags & MFI_FLAGS_1078) {
233fa1e6ef4SDoug Ambrisko 		if (!(status & MFI_1078_RM)) {
234610f2ef3SScott Long 			return 1;
235fa1e6ef4SDoug Ambrisko 		}
2360d9a4ef3SDoug Ambrisko 	}
2370d9a4ef3SDoug Ambrisko 	else if (sc->mfi_flags & MFI_FLAGS_GEN2) {
238fa1e6ef4SDoug Ambrisko 		if (!(status & MFI_GEN2_RM)) {
239fa1e6ef4SDoug Ambrisko 			return 1;
240fa1e6ef4SDoug Ambrisko 		}
241fa1e6ef4SDoug Ambrisko 	}
2420d9a4ef3SDoug Ambrisko 	else if (sc->mfi_flags & MFI_FLAGS_SKINNY) {
2430d9a4ef3SDoug Ambrisko 		if (!(status & MFI_SKINNY_RM)) {
2440d9a4ef3SDoug Ambrisko 			return 1;
2450d9a4ef3SDoug Ambrisko 		}
2460d9a4ef3SDoug Ambrisko 	}
2470d9a4ef3SDoug Ambrisko 	if (sc->mfi_flags & MFI_FLAGS_SKINNY)
2480d9a4ef3SDoug Ambrisko 		MFI_WRITE4(sc, MFI_OSTS, status);
2490d9a4ef3SDoug Ambrisko 	else
250610f2ef3SScott Long 		MFI_WRITE4(sc, MFI_ODCR0, status);
251610f2ef3SScott Long 	return 0;
252610f2ef3SScott Long }
253610f2ef3SScott Long 
254610f2ef3SScott Long static void
mfi_issue_cmd_xscale(struct mfi_softc * sc,bus_addr_t bus_add,uint32_t frame_cnt)2550d9a4ef3SDoug Ambrisko mfi_issue_cmd_xscale(struct mfi_softc *sc, bus_addr_t bus_add, uint32_t frame_cnt)
256610f2ef3SScott Long {
257610f2ef3SScott Long 	MFI_WRITE4(sc, MFI_IQP,(bus_add >>3)|frame_cnt);
258610f2ef3SScott Long }
259610f2ef3SScott Long 
260610f2ef3SScott Long static void
mfi_issue_cmd_ppc(struct mfi_softc * sc,bus_addr_t bus_add,uint32_t frame_cnt)2610d9a4ef3SDoug Ambrisko mfi_issue_cmd_ppc(struct mfi_softc *sc, bus_addr_t bus_add, uint32_t frame_cnt)
262610f2ef3SScott Long {
2630d9a4ef3SDoug Ambrisko 	if (sc->mfi_flags & MFI_FLAGS_SKINNY) {
2640d9a4ef3SDoug Ambrisko 	    MFI_WRITE4(sc, MFI_IQPL, (bus_add | frame_cnt <<1)|1 );
2650d9a4ef3SDoug Ambrisko 	    MFI_WRITE4(sc, MFI_IQPH, 0x00000000);
2660d9a4ef3SDoug Ambrisko 	} else {
267610f2ef3SScott Long 	    MFI_WRITE4(sc, MFI_IQP, (bus_add | frame_cnt <<1)|1 );
268610f2ef3SScott Long 	}
2690d9a4ef3SDoug Ambrisko }
270610f2ef3SScott Long 
2710d9a4ef3SDoug Ambrisko int
mfi_transition_firmware(struct mfi_softc * sc)2722e21a3efSScott Long mfi_transition_firmware(struct mfi_softc *sc)
2732e21a3efSScott Long {
274cadd4fa0SScott Long 	uint32_t fw_state, cur_state;
2752e21a3efSScott Long 	int max_wait, i;
2760d9a4ef3SDoug Ambrisko 	uint32_t cur_abs_reg_val = 0;
2770d9a4ef3SDoug Ambrisko 	uint32_t prev_abs_reg_val = 0;
2782e21a3efSScott Long 
2790d9a4ef3SDoug Ambrisko 	cur_abs_reg_val = sc->mfi_read_fw_status(sc);
2800d9a4ef3SDoug Ambrisko 	fw_state = cur_abs_reg_val & MFI_FWSTATE_MASK;
2812e21a3efSScott Long 	while (fw_state != MFI_FWSTATE_READY) {
2822e21a3efSScott Long 		if (bootverbose)
2832e21a3efSScott Long 			device_printf(sc->mfi_dev, "Waiting for firmware to "
2842e21a3efSScott Long 			"become ready\n");
2852e21a3efSScott Long 		cur_state = fw_state;
2862e21a3efSScott Long 		switch (fw_state) {
2872e21a3efSScott Long 		case MFI_FWSTATE_FAULT:
2882e21a3efSScott Long 			device_printf(sc->mfi_dev, "Firmware fault\n");
2892e21a3efSScott Long 			return (ENXIO);
2902e21a3efSScott Long 		case MFI_FWSTATE_WAIT_HANDSHAKE:
2910d9a4ef3SDoug Ambrisko 			if (sc->mfi_flags & MFI_FLAGS_SKINNY || sc->mfi_flags & MFI_FLAGS_TBOLT)
2920d9a4ef3SDoug Ambrisko 			    MFI_WRITE4(sc, MFI_SKINNY_IDB, MFI_FWINIT_CLEAR_HANDSHAKE);
2930d9a4ef3SDoug Ambrisko 			else
2942e21a3efSScott Long 			    MFI_WRITE4(sc, MFI_IDB, MFI_FWINIT_CLEAR_HANDSHAKE);
2950d9a4ef3SDoug Ambrisko 			max_wait = MFI_RESET_WAIT_TIME;
2962e21a3efSScott Long 			break;
2972e21a3efSScott Long 		case MFI_FWSTATE_OPERATIONAL:
2980d9a4ef3SDoug Ambrisko 			if (sc->mfi_flags & MFI_FLAGS_SKINNY || sc->mfi_flags & MFI_FLAGS_TBOLT)
2990d9a4ef3SDoug Ambrisko 			    MFI_WRITE4(sc, MFI_SKINNY_IDB, 7);
3000d9a4ef3SDoug Ambrisko 			else
3012e21a3efSScott Long 			    MFI_WRITE4(sc, MFI_IDB, MFI_FWINIT_READY);
3020d9a4ef3SDoug Ambrisko 			max_wait = MFI_RESET_WAIT_TIME;
3032e21a3efSScott Long 			break;
3042e21a3efSScott Long 		case MFI_FWSTATE_UNDEFINED:
3052e21a3efSScott Long 		case MFI_FWSTATE_BB_INIT:
3060d9a4ef3SDoug Ambrisko 			max_wait = MFI_RESET_WAIT_TIME;
3070d9a4ef3SDoug Ambrisko 			break;
3080d9a4ef3SDoug Ambrisko 		case MFI_FWSTATE_FW_INIT_2:
3090d9a4ef3SDoug Ambrisko 			max_wait = MFI_RESET_WAIT_TIME;
3102e21a3efSScott Long 			break;
3112e21a3efSScott Long 		case MFI_FWSTATE_FW_INIT:
3122e21a3efSScott Long 		case MFI_FWSTATE_FLUSH_CACHE:
3130d9a4ef3SDoug Ambrisko 			max_wait = MFI_RESET_WAIT_TIME;
3140d9a4ef3SDoug Ambrisko 			break;
3150d9a4ef3SDoug Ambrisko 		case MFI_FWSTATE_DEVICE_SCAN:
3160d9a4ef3SDoug Ambrisko 			max_wait = MFI_RESET_WAIT_TIME; /* wait for 180 seconds */
3170d9a4ef3SDoug Ambrisko 			prev_abs_reg_val = cur_abs_reg_val;
3182e21a3efSScott Long 			break;
3195dbee633SJohn Baldwin 		case MFI_FWSTATE_BOOT_MESSAGE_PENDING:
3200d9a4ef3SDoug Ambrisko 			if (sc->mfi_flags & MFI_FLAGS_SKINNY || sc->mfi_flags & MFI_FLAGS_TBOLT)
3210d9a4ef3SDoug Ambrisko 			    MFI_WRITE4(sc, MFI_SKINNY_IDB, MFI_FWINIT_HOTPLUG);
3220d9a4ef3SDoug Ambrisko 			else
3235dbee633SJohn Baldwin 			    MFI_WRITE4(sc, MFI_IDB, MFI_FWINIT_HOTPLUG);
3240d9a4ef3SDoug Ambrisko 			max_wait = MFI_RESET_WAIT_TIME;
3255dbee633SJohn Baldwin 			break;
3262e21a3efSScott Long 		default:
3275dbee633SJohn Baldwin 			device_printf(sc->mfi_dev, "Unknown firmware state %#x\n",
3282e21a3efSScott Long 			    fw_state);
3292e21a3efSScott Long 			return (ENXIO);
3302e21a3efSScott Long 		}
3312e21a3efSScott Long 		for (i = 0; i < (max_wait * 10); i++) {
3320d9a4ef3SDoug Ambrisko 			cur_abs_reg_val = sc->mfi_read_fw_status(sc);
3330d9a4ef3SDoug Ambrisko 			fw_state = cur_abs_reg_val & MFI_FWSTATE_MASK;
3342e21a3efSScott Long 			if (fw_state == cur_state)
3352e21a3efSScott Long 				DELAY(100000);
3362e21a3efSScott Long 			else
3372e21a3efSScott Long 				break;
3382e21a3efSScott Long 		}
3390d9a4ef3SDoug Ambrisko 		if (fw_state == MFI_FWSTATE_DEVICE_SCAN) {
3400d9a4ef3SDoug Ambrisko 			/* Check the device scanning progress */
3410d9a4ef3SDoug Ambrisko 			if (prev_abs_reg_val != cur_abs_reg_val) {
3420d9a4ef3SDoug Ambrisko 				continue;
3430d9a4ef3SDoug Ambrisko 			}
3440d9a4ef3SDoug Ambrisko 		}
3452e21a3efSScott Long 		if (fw_state == cur_state) {
3465dbee633SJohn Baldwin 			device_printf(sc->mfi_dev, "Firmware stuck in state "
3472e21a3efSScott Long 			    "%#x\n", fw_state);
3482e21a3efSScott Long 			return (ENXIO);
3492e21a3efSScott Long 		}
3502e21a3efSScott Long 	}
3512e21a3efSScott Long 	return (0);
3522e21a3efSScott Long }
3532e21a3efSScott Long 
3542e21a3efSScott Long static void
mfi_addr_cb(void * arg,bus_dma_segment_t * segs,int nsegs,int error)3550d9a4ef3SDoug Ambrisko mfi_addr_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
3562e21a3efSScott Long {
3570d9a4ef3SDoug Ambrisko 	bus_addr_t *addr;
3582e21a3efSScott Long 
3592e21a3efSScott Long 	addr = arg;
3602e21a3efSScott Long 	*addr = segs[0].ds_addr;
3612e21a3efSScott Long }
3622e21a3efSScott Long 
3632e21a3efSScott Long int
mfi_attach(struct mfi_softc * sc)3642e21a3efSScott Long mfi_attach(struct mfi_softc *sc)
3652e21a3efSScott Long {
3662e21a3efSScott Long 	uint32_t status;
3672e21a3efSScott Long 	int error, commsz, framessz, sensesz;
36808c89430SSteven Hartland 	int frames, unit, max_fw_sge, max_fw_cmds;
3690d9a4ef3SDoug Ambrisko 	uint32_t tb_mem_size = 0;
370b5c50320SDoug Ambrisko 	struct cdev *dev_t;
371216d58bbSDoug Ambrisko 
3720d9a4ef3SDoug Ambrisko 	if (sc == NULL)
3730d9a4ef3SDoug Ambrisko 		return EINVAL;
374216d58bbSDoug Ambrisko 
375a6ba0fd6SDoug Ambrisko 	device_printf(sc->mfi_dev, "Megaraid SAS driver Ver %s \n",
376a6ba0fd6SDoug Ambrisko 	    MEGASAS_VERSION);
3772e21a3efSScott Long 
3782e21a3efSScott Long 	mtx_init(&sc->mfi_io_lock, "MFI I/O lock", NULL, MTX_DEF);
3798ec5c98bSJohn Baldwin 	sx_init(&sc->mfi_config_lock, "MFI config");
3802e21a3efSScott Long 	TAILQ_INIT(&sc->mfi_ld_tqh);
3810d9a4ef3SDoug Ambrisko 	TAILQ_INIT(&sc->mfi_syspd_tqh);
38258ef3154SDoug Ambrisko 	TAILQ_INIT(&sc->mfi_ld_pend_tqh);
38358ef3154SDoug Ambrisko 	TAILQ_INIT(&sc->mfi_syspd_pend_tqh);
384a6ba0fd6SDoug Ambrisko 	TAILQ_INIT(&sc->mfi_evt_queue);
385a6ba0fd6SDoug Ambrisko 	TASK_INIT(&sc->mfi_evt_task, 0, mfi_handle_evt, sc);
386ddbffe7fSDoug Ambrisko 	TASK_INIT(&sc->mfi_map_sync_task, 0, mfi_handle_map_sync, sc);
387741367d5SDoug Ambrisko 	TAILQ_INIT(&sc->mfi_aen_pids);
38835ef86f2SScott Long 	TAILQ_INIT(&sc->mfi_cam_ccbq);
3892e21a3efSScott Long 
3902e21a3efSScott Long 	mfi_initq_free(sc);
3912e21a3efSScott Long 	mfi_initq_ready(sc);
3922e21a3efSScott Long 	mfi_initq_busy(sc);
3932e21a3efSScott Long 	mfi_initq_bio(sc);
3942e21a3efSScott Long 
3950d9a4ef3SDoug Ambrisko 	sc->adpreset = 0;
3960d9a4ef3SDoug Ambrisko 	sc->last_seq_num = 0;
3970d9a4ef3SDoug Ambrisko 	sc->disableOnlineCtrlReset = 1;
3980d9a4ef3SDoug Ambrisko 	sc->issuepend_done = 1;
3990d9a4ef3SDoug Ambrisko 	sc->hw_crit_error = 0;
4000d9a4ef3SDoug Ambrisko 
401610f2ef3SScott Long 	if (sc->mfi_flags & MFI_FLAGS_1064R) {
402610f2ef3SScott Long 		sc->mfi_enable_intr = mfi_enable_intr_xscale;
403610f2ef3SScott Long 		sc->mfi_read_fw_status = mfi_read_fw_status_xscale;
404610f2ef3SScott Long 		sc->mfi_check_clear_intr = mfi_check_clear_intr_xscale;
405610f2ef3SScott Long 		sc->mfi_issue_cmd = mfi_issue_cmd_xscale;
406a6ba0fd6SDoug Ambrisko 	} else if (sc->mfi_flags & MFI_FLAGS_TBOLT) {
4070d9a4ef3SDoug Ambrisko 		sc->mfi_enable_intr = mfi_tbolt_enable_intr_ppc;
4080d9a4ef3SDoug Ambrisko 		sc->mfi_disable_intr = mfi_tbolt_disable_intr_ppc;
4090d9a4ef3SDoug Ambrisko 		sc->mfi_read_fw_status = mfi_tbolt_read_fw_status_ppc;
4100d9a4ef3SDoug Ambrisko 		sc->mfi_check_clear_intr = mfi_tbolt_check_clear_intr_ppc;
4110d9a4ef3SDoug Ambrisko 		sc->mfi_issue_cmd = mfi_tbolt_issue_cmd_ppc;
4120d9a4ef3SDoug Ambrisko 		sc->mfi_adp_reset = mfi_tbolt_adp_reset;
4130d9a4ef3SDoug Ambrisko 		sc->mfi_tbolt = 1;
4140d9a4ef3SDoug Ambrisko 		TAILQ_INIT(&sc->mfi_cmd_tbolt_tqh);
415a6ba0fd6SDoug Ambrisko 	} else {
416610f2ef3SScott Long 		sc->mfi_enable_intr =  mfi_enable_intr_ppc;
417610f2ef3SScott Long 		sc->mfi_read_fw_status = mfi_read_fw_status_ppc;
418610f2ef3SScott Long 		sc->mfi_check_clear_intr = mfi_check_clear_intr_ppc;
419610f2ef3SScott Long 		sc->mfi_issue_cmd = mfi_issue_cmd_ppc;
420610f2ef3SScott Long 	}
421610f2ef3SScott Long 
4222e21a3efSScott Long 	/* Before we get too far, see if the firmware is working */
4232e21a3efSScott Long 	if ((error = mfi_transition_firmware(sc)) != 0) {
4242e21a3efSScott Long 		device_printf(sc->mfi_dev, "Firmware not in READY state, "
4252e21a3efSScott Long 		    "error %d\n", error);
4262e21a3efSScott Long 		return (ENXIO);
4272e21a3efSScott Long 	}
4282e21a3efSScott Long 
429a6ba0fd6SDoug Ambrisko 	/* Start: LSIP200113393 */
4300d9a4ef3SDoug Ambrisko 	if (bus_dma_tag_create( sc->mfi_parent_dmat,	/* parent */
4310d9a4ef3SDoug Ambrisko 				1, 0,			/* algnmnt, boundary */
4320d9a4ef3SDoug Ambrisko 				BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
4330d9a4ef3SDoug Ambrisko 				BUS_SPACE_MAXADDR,	/* highaddr */
4340d9a4ef3SDoug Ambrisko 				NULL, NULL,		/* filter, filterarg */
4350d9a4ef3SDoug Ambrisko 				MEGASAS_MAX_NAME*sizeof(bus_addr_t),			/* maxsize */
4360d9a4ef3SDoug Ambrisko 				1,			/* msegments */
4370d9a4ef3SDoug Ambrisko 				MEGASAS_MAX_NAME*sizeof(bus_addr_t),			/* maxsegsize */
4380d9a4ef3SDoug Ambrisko 				0,			/* flags */
4390d9a4ef3SDoug Ambrisko 				NULL, NULL,		/* lockfunc, lockarg */
4400d9a4ef3SDoug Ambrisko 				&sc->verbuf_h_dmat)) {
4410d9a4ef3SDoug Ambrisko 		device_printf(sc->mfi_dev, "Cannot allocate verbuf_h_dmat DMA tag\n");
4420d9a4ef3SDoug Ambrisko 		return (ENOMEM);
4430d9a4ef3SDoug Ambrisko 	}
4440d9a4ef3SDoug Ambrisko 	if (bus_dmamem_alloc(sc->verbuf_h_dmat, (void **)&sc->verbuf,
4450d9a4ef3SDoug Ambrisko 	    BUS_DMA_NOWAIT, &sc->verbuf_h_dmamap)) {
4460d9a4ef3SDoug Ambrisko 		device_printf(sc->mfi_dev, "Cannot allocate verbuf_h_dmamap memory\n");
4470d9a4ef3SDoug Ambrisko 		return (ENOMEM);
4480d9a4ef3SDoug Ambrisko 	}
4490d9a4ef3SDoug Ambrisko 	bzero(sc->verbuf, MEGASAS_MAX_NAME*sizeof(bus_addr_t));
4500d9a4ef3SDoug Ambrisko 	bus_dmamap_load(sc->verbuf_h_dmat, sc->verbuf_h_dmamap,
451a6ba0fd6SDoug Ambrisko 	    sc->verbuf, MEGASAS_MAX_NAME*sizeof(bus_addr_t),
452a6ba0fd6SDoug Ambrisko 	    mfi_addr_cb, &sc->verbuf_h_busaddr, 0);
453a6ba0fd6SDoug Ambrisko 	/* End: LSIP200113393 */
4542e21a3efSScott Long 
4552e21a3efSScott Long 	/*
4562e21a3efSScott Long 	 * Get information needed for sizing the contiguous memory for the
4572e21a3efSScott Long 	 * frame pool.  Size down the sgl parameter since we know that
458cd853791SKonstantin Belousov 	 * we will never need more than what's required for MFI_MAXPHYS.
4592e21a3efSScott Long 	 * It would be nice if these constants were available at runtime
4602e21a3efSScott Long 	 * instead of compile time.
4612e21a3efSScott Long 	 */
462610f2ef3SScott Long 	status = sc->mfi_read_fw_status(sc);
46308c89430SSteven Hartland 	max_fw_cmds = status & MFI_FWSTATE_MAXCMD_MASK;
46408c89430SSteven Hartland 	if (mfi_max_cmds > 0 && mfi_max_cmds < max_fw_cmds) {
46508c89430SSteven Hartland 		device_printf(sc->mfi_dev, "FW MaxCmds = %d, limiting to %d\n",
46608c89430SSteven Hartland 		    max_fw_cmds, mfi_max_cmds);
46708c89430SSteven Hartland 		sc->mfi_max_fw_cmds = mfi_max_cmds;
46808c89430SSteven Hartland 	} else {
46908c89430SSteven Hartland 		sc->mfi_max_fw_cmds = max_fw_cmds;
47008c89430SSteven Hartland 	}
47178e36c27SScott Long 	max_fw_sge = (status & MFI_FWSTATE_MAXSGL_MASK) >> 16;
47252c9ce25SScott Long 	sc->mfi_max_sge = min(max_fw_sge, ((MFI_MAXPHYS / PAGE_SIZE) + 1));
4732e21a3efSScott Long 
4740d9a4ef3SDoug Ambrisko 	/* ThunderBolt Support get the contiguous memory */
4750d9a4ef3SDoug Ambrisko 
4760d9a4ef3SDoug Ambrisko 	if (sc->mfi_flags & MFI_FLAGS_TBOLT) {
4770d9a4ef3SDoug Ambrisko 		mfi_tbolt_init_globals(sc);
47808c89430SSteven Hartland 		device_printf(sc->mfi_dev, "MaxCmd = %d, Drv MaxCmd = %d, "
47908c89430SSteven Hartland 		    "MaxSgl = %d, state = %#x\n", max_fw_cmds,
4800d9a4ef3SDoug Ambrisko 		    sc->mfi_max_fw_cmds, sc->mfi_max_sge, status);
4810d9a4ef3SDoug Ambrisko 		tb_mem_size = mfi_tbolt_get_memory_requirement(sc);
4820d9a4ef3SDoug Ambrisko 
4830d9a4ef3SDoug Ambrisko 		if (bus_dma_tag_create( sc->mfi_parent_dmat,	/* parent */
4840d9a4ef3SDoug Ambrisko 				1, 0,			/* algnmnt, boundary */
4850d9a4ef3SDoug Ambrisko 				BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
4860d9a4ef3SDoug Ambrisko 				BUS_SPACE_MAXADDR,	/* highaddr */
4870d9a4ef3SDoug Ambrisko 				NULL, NULL,		/* filter, filterarg */
4880d9a4ef3SDoug Ambrisko 				tb_mem_size,		/* maxsize */
4890d9a4ef3SDoug Ambrisko 				1,			/* msegments */
4900d9a4ef3SDoug Ambrisko 				tb_mem_size,		/* maxsegsize */
4910d9a4ef3SDoug Ambrisko 				0,			/* flags */
4920d9a4ef3SDoug Ambrisko 				NULL, NULL,		/* lockfunc, lockarg */
4930d9a4ef3SDoug Ambrisko 				&sc->mfi_tb_dmat)) {
4940d9a4ef3SDoug Ambrisko 			device_printf(sc->mfi_dev, "Cannot allocate comms DMA tag\n");
4950d9a4ef3SDoug Ambrisko 			return (ENOMEM);
4960d9a4ef3SDoug Ambrisko 		}
4970d9a4ef3SDoug Ambrisko 		if (bus_dmamem_alloc(sc->mfi_tb_dmat, (void **)&sc->request_message_pool,
4980d9a4ef3SDoug Ambrisko 		BUS_DMA_NOWAIT, &sc->mfi_tb_dmamap)) {
4990d9a4ef3SDoug Ambrisko 			device_printf(sc->mfi_dev, "Cannot allocate comms memory\n");
5000d9a4ef3SDoug Ambrisko 			return (ENOMEM);
5010d9a4ef3SDoug Ambrisko 		}
5020d9a4ef3SDoug Ambrisko 		bzero(sc->request_message_pool, tb_mem_size);
5030d9a4ef3SDoug Ambrisko 		bus_dmamap_load(sc->mfi_tb_dmat, sc->mfi_tb_dmamap,
5040d9a4ef3SDoug Ambrisko 		sc->request_message_pool, tb_mem_size, mfi_addr_cb, &sc->mfi_tb_busaddr, 0);
5050d9a4ef3SDoug Ambrisko 
5060d9a4ef3SDoug Ambrisko 		/* For ThunderBolt memory init */
5070d9a4ef3SDoug Ambrisko 		if (bus_dma_tag_create( sc->mfi_parent_dmat,	/* parent */
508a6ba0fd6SDoug Ambrisko 				0x100, 0,		/* alignmnt, boundary */
5090d9a4ef3SDoug Ambrisko 				BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
5100d9a4ef3SDoug Ambrisko 				BUS_SPACE_MAXADDR,	/* highaddr */
5110d9a4ef3SDoug Ambrisko 				NULL, NULL,		/* filter, filterarg */
5120d9a4ef3SDoug Ambrisko 				MFI_FRAME_SIZE,		/* maxsize */
5130d9a4ef3SDoug Ambrisko 				1,			/* msegments */
5140d9a4ef3SDoug Ambrisko 				MFI_FRAME_SIZE,		/* maxsegsize */
5150d9a4ef3SDoug Ambrisko 				0,			/* flags */
5160d9a4ef3SDoug Ambrisko 				NULL, NULL,		/* lockfunc, lockarg */
5170d9a4ef3SDoug Ambrisko 				&sc->mfi_tb_init_dmat)) {
5180d9a4ef3SDoug Ambrisko 			device_printf(sc->mfi_dev, "Cannot allocate init DMA tag\n");
5190d9a4ef3SDoug Ambrisko 			return (ENOMEM);
5200d9a4ef3SDoug Ambrisko 		}
5210d9a4ef3SDoug Ambrisko 		if (bus_dmamem_alloc(sc->mfi_tb_init_dmat, (void **)&sc->mfi_tb_init,
5220d9a4ef3SDoug Ambrisko 		    BUS_DMA_NOWAIT, &sc->mfi_tb_init_dmamap)) {
5230d9a4ef3SDoug Ambrisko 			device_printf(sc->mfi_dev, "Cannot allocate init memory\n");
5240d9a4ef3SDoug Ambrisko 			return (ENOMEM);
5250d9a4ef3SDoug Ambrisko 		}
5260d9a4ef3SDoug Ambrisko 		bzero(sc->mfi_tb_init, MFI_FRAME_SIZE);
5270d9a4ef3SDoug Ambrisko 		bus_dmamap_load(sc->mfi_tb_init_dmat, sc->mfi_tb_init_dmamap,
528a6ba0fd6SDoug Ambrisko 		sc->mfi_tb_init, MFI_FRAME_SIZE, mfi_addr_cb,
529a6ba0fd6SDoug Ambrisko 		    &sc->mfi_tb_init_busaddr, 0);
530a6ba0fd6SDoug Ambrisko 		if (mfi_tbolt_init_desc_pool(sc, sc->request_message_pool,
531a6ba0fd6SDoug Ambrisko 		    tb_mem_size)) {
532a6ba0fd6SDoug Ambrisko 			device_printf(sc->mfi_dev,
533a6ba0fd6SDoug Ambrisko 			    "Thunderbolt pool preparation error\n");
5340d9a4ef3SDoug Ambrisko 			return 0;
5350d9a4ef3SDoug Ambrisko 		}
5360d9a4ef3SDoug Ambrisko 
5370d9a4ef3SDoug Ambrisko 		/*
5380d9a4ef3SDoug Ambrisko 		  Allocate DMA memory mapping for MPI2 IOC Init descriptor,
539453130d9SPedro F. Giffuni 		  we are taking it different from what we have allocated for Request
5400d9a4ef3SDoug Ambrisko 		  and reply descriptors to avoid confusion later
5410d9a4ef3SDoug Ambrisko 		*/
5420d9a4ef3SDoug Ambrisko 		tb_mem_size = sizeof(struct MPI2_IOC_INIT_REQUEST);
5430d9a4ef3SDoug Ambrisko 		if (bus_dma_tag_create( sc->mfi_parent_dmat,	/* parent */
5440d9a4ef3SDoug Ambrisko 				1, 0,			/* algnmnt, boundary */
5450d9a4ef3SDoug Ambrisko 				BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
5460d9a4ef3SDoug Ambrisko 				BUS_SPACE_MAXADDR,	/* highaddr */
5470d9a4ef3SDoug Ambrisko 				NULL, NULL,		/* filter, filterarg */
5480d9a4ef3SDoug Ambrisko 				tb_mem_size,		/* maxsize */
5490d9a4ef3SDoug Ambrisko 				1,			/* msegments */
5500d9a4ef3SDoug Ambrisko 				tb_mem_size,		/* maxsegsize */
5510d9a4ef3SDoug Ambrisko 				0,			/* flags */
5520d9a4ef3SDoug Ambrisko 				NULL, NULL,		/* lockfunc, lockarg */
5530d9a4ef3SDoug Ambrisko 				&sc->mfi_tb_ioc_init_dmat)) {
554a6ba0fd6SDoug Ambrisko 			device_printf(sc->mfi_dev,
555a6ba0fd6SDoug Ambrisko 			    "Cannot allocate comms DMA tag\n");
5560d9a4ef3SDoug Ambrisko 			return (ENOMEM);
5570d9a4ef3SDoug Ambrisko 		}
558a6ba0fd6SDoug Ambrisko 		if (bus_dmamem_alloc(sc->mfi_tb_ioc_init_dmat,
559a6ba0fd6SDoug Ambrisko 		    (void **)&sc->mfi_tb_ioc_init_desc,
5600d9a4ef3SDoug Ambrisko 		    BUS_DMA_NOWAIT, &sc->mfi_tb_ioc_init_dmamap)) {
5610d9a4ef3SDoug Ambrisko 			device_printf(sc->mfi_dev, "Cannot allocate comms memory\n");
5620d9a4ef3SDoug Ambrisko 			return (ENOMEM);
5630d9a4ef3SDoug Ambrisko 		}
5640d9a4ef3SDoug Ambrisko 		bzero(sc->mfi_tb_ioc_init_desc, tb_mem_size);
5650d9a4ef3SDoug Ambrisko 		bus_dmamap_load(sc->mfi_tb_ioc_init_dmat, sc->mfi_tb_ioc_init_dmamap,
566a6ba0fd6SDoug Ambrisko 		sc->mfi_tb_ioc_init_desc, tb_mem_size, mfi_addr_cb,
567a6ba0fd6SDoug Ambrisko 		    &sc->mfi_tb_ioc_init_busaddr, 0);
5680d9a4ef3SDoug Ambrisko 	}
5692e21a3efSScott Long 	/*
5702e21a3efSScott Long 	 * Create the dma tag for data buffers.  Used both for block I/O
5712e21a3efSScott Long 	 * and for various internal data queries.
5722e21a3efSScott Long 	 */
5732e21a3efSScott Long 	if (bus_dma_tag_create( sc->mfi_parent_dmat,	/* parent */
5742e21a3efSScott Long 				1, 0,			/* algnmnt, boundary */
5752e21a3efSScott Long 				BUS_SPACE_MAXADDR,	/* lowaddr */
5762e21a3efSScott Long 				BUS_SPACE_MAXADDR,	/* highaddr */
5772e21a3efSScott Long 				NULL, NULL,		/* filter, filterarg */
5782e21a3efSScott Long 				BUS_SPACE_MAXSIZE_32BIT,/* maxsize */
57978e36c27SScott Long 				sc->mfi_max_sge,	/* nsegments */
5802e21a3efSScott Long 				BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
5812e21a3efSScott Long 				BUS_DMA_ALLOCNOW,	/* flags */
5822e21a3efSScott Long 				busdma_lock_mutex,	/* lockfunc */
5832e21a3efSScott Long 				&sc->mfi_io_lock,	/* lockfuncarg */
5842e21a3efSScott Long 				&sc->mfi_buffer_dmat)) {
5852e21a3efSScott Long 		device_printf(sc->mfi_dev, "Cannot allocate buffer DMA tag\n");
5862e21a3efSScott Long 		return (ENOMEM);
5872e21a3efSScott Long 	}
5882e21a3efSScott Long 
5892e21a3efSScott Long 	/*
5902e21a3efSScott Long 	 * Allocate DMA memory for the comms queues.  Keep it under 4GB for
5912e21a3efSScott Long 	 * efficiency.  The mfi_hwcomms struct includes space for 1 reply queue
5922e21a3efSScott Long 	 * entry, so the calculated size here will be will be 1 more than
5932e21a3efSScott Long 	 * mfi_max_fw_cmds.  This is apparently a requirement of the hardware.
5942e21a3efSScott Long 	 */
5952e21a3efSScott Long 	commsz = (sizeof(uint32_t) * sc->mfi_max_fw_cmds) +
5962e21a3efSScott Long 	    sizeof(struct mfi_hwcomms);
5972e21a3efSScott Long 	if (bus_dma_tag_create( sc->mfi_parent_dmat,	/* parent */
5982e21a3efSScott Long 				1, 0,			/* algnmnt, boundary */
5992e21a3efSScott Long 				BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
6002e21a3efSScott Long 				BUS_SPACE_MAXADDR,	/* highaddr */
6012e21a3efSScott Long 				NULL, NULL,		/* filter, filterarg */
6022e21a3efSScott Long 				commsz,			/* maxsize */
6032e21a3efSScott Long 				1,			/* msegments */
6042e21a3efSScott Long 				commsz,			/* maxsegsize */
6052e21a3efSScott Long 				0,			/* flags */
6062e21a3efSScott Long 				NULL, NULL,		/* lockfunc, lockarg */
6072e21a3efSScott Long 				&sc->mfi_comms_dmat)) {
6082e21a3efSScott Long 		device_printf(sc->mfi_dev, "Cannot allocate comms DMA tag\n");
6092e21a3efSScott Long 		return (ENOMEM);
6102e21a3efSScott Long 	}
6112e21a3efSScott Long 	if (bus_dmamem_alloc(sc->mfi_comms_dmat, (void **)&sc->mfi_comms,
6122e21a3efSScott Long 	    BUS_DMA_NOWAIT, &sc->mfi_comms_dmamap)) {
6132e21a3efSScott Long 		device_printf(sc->mfi_dev, "Cannot allocate comms memory\n");
6142e21a3efSScott Long 		return (ENOMEM);
6152e21a3efSScott Long 	}
6162e21a3efSScott Long 	bzero(sc->mfi_comms, commsz);
6172e21a3efSScott Long 	bus_dmamap_load(sc->mfi_comms_dmat, sc->mfi_comms_dmamap,
6180d9a4ef3SDoug Ambrisko 	    sc->mfi_comms, commsz, mfi_addr_cb, &sc->mfi_comms_busaddr, 0);
6192e21a3efSScott Long 	/*
6202e21a3efSScott Long 	 * Allocate DMA memory for the command frames.  Keep them in the
62178e36c27SScott Long 	 * lower 4GB for efficiency.  Calculate the size of the commands at
62278e36c27SScott Long 	 * the same time; each command is one 64 byte frame plus a set of
62378e36c27SScott Long          * additional frames for holding sg lists or other data.
6242e21a3efSScott Long 	 * The assumption here is that the SG list will start at the second
62578e36c27SScott Long 	 * frame and not use the unused bytes in the first frame.  While this
62678e36c27SScott Long 	 * isn't technically correct, it simplifies the calculation and allows
62778e36c27SScott Long 	 * for command frames that might be larger than an mfi_io_frame.
6282e21a3efSScott Long 	 */
6292e21a3efSScott Long 	if (sizeof(bus_addr_t) == 8) {
63078e36c27SScott Long 		sc->mfi_sge_size = sizeof(struct mfi_sg64);
6312e21a3efSScott Long 		sc->mfi_flags |= MFI_FLAGS_SG64;
6322e21a3efSScott Long 	} else {
63378e36c27SScott Long 		sc->mfi_sge_size = sizeof(struct mfi_sg32);
6342e21a3efSScott Long 	}
6350d9a4ef3SDoug Ambrisko 	if (sc->mfi_flags & MFI_FLAGS_SKINNY)
6360d9a4ef3SDoug Ambrisko 		sc->mfi_sge_size = sizeof(struct mfi_sg_skinny);
63778e36c27SScott Long 	frames = (sc->mfi_sge_size * sc->mfi_max_sge - 1) / MFI_FRAME_SIZE + 2;
63878e36c27SScott Long 	sc->mfi_cmd_size = frames * MFI_FRAME_SIZE;
63978e36c27SScott Long 	framessz = sc->mfi_cmd_size * sc->mfi_max_fw_cmds;
6402e21a3efSScott Long 	if (bus_dma_tag_create( sc->mfi_parent_dmat,	/* parent */
6412e21a3efSScott Long 				64, 0,			/* algnmnt, boundary */
6422e21a3efSScott Long 				BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
6432e21a3efSScott Long 				BUS_SPACE_MAXADDR,	/* highaddr */
6442e21a3efSScott Long 				NULL, NULL,		/* filter, filterarg */
6452e21a3efSScott Long 				framessz,		/* maxsize */
6462e21a3efSScott Long 				1,			/* nsegments */
6472e21a3efSScott Long 				framessz,		/* maxsegsize */
6482e21a3efSScott Long 				0,			/* flags */
6492e21a3efSScott Long 				NULL, NULL,		/* lockfunc, lockarg */
6502e21a3efSScott Long 				&sc->mfi_frames_dmat)) {
6512e21a3efSScott Long 		device_printf(sc->mfi_dev, "Cannot allocate frame DMA tag\n");
6522e21a3efSScott Long 		return (ENOMEM);
6532e21a3efSScott Long 	}
6542e21a3efSScott Long 	if (bus_dmamem_alloc(sc->mfi_frames_dmat, (void **)&sc->mfi_frames,
6552e21a3efSScott Long 	    BUS_DMA_NOWAIT, &sc->mfi_frames_dmamap)) {
6562e21a3efSScott Long 		device_printf(sc->mfi_dev, "Cannot allocate frames memory\n");
6572e21a3efSScott Long 		return (ENOMEM);
6582e21a3efSScott Long 	}
6592e21a3efSScott Long 	bzero(sc->mfi_frames, framessz);
6602e21a3efSScott Long 	bus_dmamap_load(sc->mfi_frames_dmat, sc->mfi_frames_dmamap,
6610d9a4ef3SDoug Ambrisko 	    sc->mfi_frames, framessz, mfi_addr_cb, &sc->mfi_frames_busaddr,0);
6622e21a3efSScott Long 	/*
6632e21a3efSScott Long 	 * Allocate DMA memory for the frame sense data.  Keep them in the
6642e21a3efSScott Long 	 * lower 4GB for efficiency
6652e21a3efSScott Long 	 */
6662e21a3efSScott Long 	sensesz = sc->mfi_max_fw_cmds * MFI_SENSE_LEN;
6672e21a3efSScott Long 	if (bus_dma_tag_create( sc->mfi_parent_dmat,	/* parent */
6682e21a3efSScott Long 				4, 0,			/* algnmnt, boundary */
6692e21a3efSScott Long 				BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
6702e21a3efSScott Long 				BUS_SPACE_MAXADDR,	/* highaddr */
6712e21a3efSScott Long 				NULL, NULL,		/* filter, filterarg */
6722e21a3efSScott Long 				sensesz,		/* maxsize */
6732e21a3efSScott Long 				1,			/* nsegments */
6742e21a3efSScott Long 				sensesz,		/* maxsegsize */
6752e21a3efSScott Long 				0,			/* flags */
6762e21a3efSScott Long 				NULL, NULL,		/* lockfunc, lockarg */
6772e21a3efSScott Long 				&sc->mfi_sense_dmat)) {
6782e21a3efSScott Long 		device_printf(sc->mfi_dev, "Cannot allocate sense DMA tag\n");
6792e21a3efSScott Long 		return (ENOMEM);
6802e21a3efSScott Long 	}
6812e21a3efSScott Long 	if (bus_dmamem_alloc(sc->mfi_sense_dmat, (void **)&sc->mfi_sense,
6822e21a3efSScott Long 	    BUS_DMA_NOWAIT, &sc->mfi_sense_dmamap)) {
6832e21a3efSScott Long 		device_printf(sc->mfi_dev, "Cannot allocate sense memory\n");
6842e21a3efSScott Long 		return (ENOMEM);
6852e21a3efSScott Long 	}
6862e21a3efSScott Long 	bus_dmamap_load(sc->mfi_sense_dmat, sc->mfi_sense_dmamap,
6870d9a4ef3SDoug Ambrisko 	    sc->mfi_sense, sensesz, mfi_addr_cb, &sc->mfi_sense_busaddr, 0);
6882e21a3efSScott Long 	if ((error = mfi_alloc_commands(sc)) != 0)
6892e21a3efSScott Long 		return (error);
6902e21a3efSScott Long 
6910d9a4ef3SDoug Ambrisko 	/* Before moving the FW to operational state, check whether
6920d9a4ef3SDoug Ambrisko 	 * hostmemory is required by the FW or not
6932e21a3efSScott Long 	 */
6940d9a4ef3SDoug Ambrisko 
6950d9a4ef3SDoug Ambrisko 	/* ThunderBolt MFI_IOC2 INIT */
696a6ba0fd6SDoug Ambrisko 	if (sc->mfi_flags & MFI_FLAGS_TBOLT) {
6970d9a4ef3SDoug Ambrisko 		sc->mfi_disable_intr(sc);
69808c89430SSteven Hartland 		mtx_lock(&sc->mfi_io_lock);
699a6ba0fd6SDoug Ambrisko 		if ((error = mfi_tbolt_init_MFI_queue(sc)) != 0) {
700a6ba0fd6SDoug Ambrisko 			device_printf(sc->mfi_dev,
701a6ba0fd6SDoug Ambrisko 			    "TB Init has failed with error %d\n",error);
70208c89430SSteven Hartland 			mtx_unlock(&sc->mfi_io_lock);
7030d9a4ef3SDoug Ambrisko 			return error;
7040d9a4ef3SDoug Ambrisko 		}
70508c89430SSteven Hartland 		mtx_unlock(&sc->mfi_io_lock);
7060d9a4ef3SDoug Ambrisko 
7070d9a4ef3SDoug Ambrisko 		if ((error = mfi_tbolt_alloc_cmd(sc)) != 0)
7080d9a4ef3SDoug Ambrisko 			return error;
709a6ba0fd6SDoug Ambrisko 		if (bus_setup_intr(sc->mfi_dev, sc->mfi_irq,
710a6ba0fd6SDoug Ambrisko 		    INTR_MPSAFE|INTR_TYPE_BIO, NULL, mfi_intr_tbolt, sc,
711a6ba0fd6SDoug Ambrisko 		    &sc->mfi_intr)) {
7122e21a3efSScott Long 			device_printf(sc->mfi_dev, "Cannot set up interrupt\n");
7132e21a3efSScott Long 			return (EINVAL);
7142e21a3efSScott Long 		}
71558ef3154SDoug Ambrisko 		sc->mfi_intr_ptr = mfi_intr_tbolt;
7160d9a4ef3SDoug Ambrisko 		sc->mfi_enable_intr(sc);
717a6ba0fd6SDoug Ambrisko 	} else {
7182e21a3efSScott Long 		if ((error = mfi_comms_init(sc)) != 0)
7192e21a3efSScott Long 			return (error);
7202e21a3efSScott Long 
721a6ba0fd6SDoug Ambrisko 		if (bus_setup_intr(sc->mfi_dev, sc->mfi_irq,
722a6ba0fd6SDoug Ambrisko 		    INTR_MPSAFE|INTR_TYPE_BIO, NULL, mfi_intr, sc, &sc->mfi_intr)) {
7232e21a3efSScott Long 			device_printf(sc->mfi_dev, "Cannot set up interrupt\n");
7242e21a3efSScott Long 			return (EINVAL);
7252e21a3efSScott Long 		}
72658ef3154SDoug Ambrisko 		sc->mfi_intr_ptr = mfi_intr;
7270d9a4ef3SDoug Ambrisko 		sc->mfi_enable_intr(sc);
7280d9a4ef3SDoug Ambrisko 	}
7290d9a4ef3SDoug Ambrisko 	if ((error = mfi_get_controller_info(sc)) != 0)
7300d9a4ef3SDoug Ambrisko 		return (error);
7310d9a4ef3SDoug Ambrisko 	sc->disableOnlineCtrlReset = 0;
7322e21a3efSScott Long 
7332e21a3efSScott Long 	/* Register a config hook to probe the bus for arrays */
7342e21a3efSScott Long 	sc->mfi_ich.ich_func = mfi_startup;
7352e21a3efSScott Long 	sc->mfi_ich.ich_arg = sc;
7362e21a3efSScott Long 	if (config_intrhook_establish(&sc->mfi_ich) != 0) {
7372e21a3efSScott Long 		device_printf(sc->mfi_dev, "Cannot establish configuration "
7382e21a3efSScott Long 		    "hook\n");
7392e21a3efSScott Long 		return (EINVAL);
7402e21a3efSScott Long 	}
74108c89430SSteven Hartland 	mtx_lock(&sc->mfi_io_lock);
74208c89430SSteven Hartland 	if ((error = mfi_aen_setup(sc, 0), 0) != 0) {
74308c89430SSteven Hartland 		mtx_unlock(&sc->mfi_io_lock);
7440d9a4ef3SDoug Ambrisko 		return (error);
74508c89430SSteven Hartland 	}
74608c89430SSteven Hartland 	mtx_unlock(&sc->mfi_io_lock);
7472e21a3efSScott Long 
7482e21a3efSScott Long 	/*
7492e21a3efSScott Long 	 * Register a shutdown handler.
7502e21a3efSScott Long 	 */
7512e21a3efSScott Long 	if ((sc->mfi_eh = EVENTHANDLER_REGISTER(shutdown_final, mfi_shutdown,
7522e21a3efSScott Long 	    sc, SHUTDOWN_PRI_DEFAULT)) == NULL) {
7532e21a3efSScott Long 		device_printf(sc->mfi_dev, "Warning: shutdown event "
7542e21a3efSScott Long 		    "registration failed\n");
7552e21a3efSScott Long 	}
7562e21a3efSScott Long 
7572e21a3efSScott Long 	/*
7582e21a3efSScott Long 	 * Create the control device for doing management
7592e21a3efSScott Long 	 */
7602e21a3efSScott Long 	unit = device_get_unit(sc->mfi_dev);
7612e21a3efSScott Long 	sc->mfi_cdev = make_dev(&mfi_cdevsw, unit, UID_ROOT, GID_OPERATOR,
7622e21a3efSScott Long 	    0640, "mfi%d", unit);
763741367d5SDoug Ambrisko 	if (unit == 0)
764b5c50320SDoug Ambrisko 		make_dev_alias_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK, &dev_t,
765b5c50320SDoug Ambrisko 		    sc->mfi_cdev, "%s", "megaraid_sas_ioctl_node");
7662e21a3efSScott Long 	if (sc->mfi_cdev != NULL)
7672e21a3efSScott Long 		sc->mfi_cdev->si_drv1 = sc;
7688ec5c98bSJohn Baldwin 	SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->mfi_dev),
7698ec5c98bSJohn Baldwin 	    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->mfi_dev)),
7708ec5c98bSJohn Baldwin 	    OID_AUTO, "delete_busy_volumes", CTLFLAG_RW,
7718ec5c98bSJohn Baldwin 	    &sc->mfi_delete_busy_volumes, 0, "Allow removal of busy volumes");
7728ec5c98bSJohn Baldwin 	SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->mfi_dev),
7738ec5c98bSJohn Baldwin 	    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->mfi_dev)),
7748ec5c98bSJohn Baldwin 	    OID_AUTO, "keep_deleted_volumes", CTLFLAG_RW,
7758ec5c98bSJohn Baldwin 	    &sc->mfi_keep_deleted_volumes, 0,
7768ec5c98bSJohn Baldwin 	    "Don't detach the mfid device for a busy volume that is deleted");
7772e21a3efSScott Long 
7785b56413dSWarner Losh 	device_add_child(sc->mfi_dev, "mfip", DEVICE_UNIT_ANY);
77918250ec6SJohn Baldwin 	bus_attach_children(sc->mfi_dev);
78035ef86f2SScott Long 
7815ba21ff1SScott Long 	/* Start the timeout watchdog */
782fd90e2edSJung-uk Kim 	callout_init(&sc->mfi_watchdog_callout, 1);
783f108bdaaSSteven Hartland 	callout_reset(&sc->mfi_watchdog_callout, mfi_cmd_timeout * hz,
7845ba21ff1SScott Long 	    mfi_timeout, sc);
7855ba21ff1SScott Long 
786ddbffe7fSDoug Ambrisko 	if (sc->mfi_flags & MFI_FLAGS_TBOLT) {
78708c89430SSteven Hartland 		mtx_lock(&sc->mfi_io_lock);
788ddbffe7fSDoug Ambrisko 		mfi_tbolt_sync_map_info(sc);
78908c89430SSteven Hartland 		mtx_unlock(&sc->mfi_io_lock);
790ddbffe7fSDoug Ambrisko 	}
791ddbffe7fSDoug Ambrisko 
7922e21a3efSScott Long 	return (0);
7932e21a3efSScott Long }
7942e21a3efSScott Long 
7952e21a3efSScott Long static int
mfi_alloc_commands(struct mfi_softc * sc)7962e21a3efSScott Long mfi_alloc_commands(struct mfi_softc *sc)
7972e21a3efSScott Long {
7982e21a3efSScott Long 	struct mfi_command *cm;
79908c89430SSteven Hartland 	int i, j;
8002e21a3efSScott Long 
8012e21a3efSScott Long 	/*
8022e21a3efSScott Long 	 * XXX Should we allocate all the commands up front, or allocate on
8032e21a3efSScott Long 	 * demand later like 'aac' does?
8042e21a3efSScott Long 	 */
80508c89430SSteven Hartland 	sc->mfi_commands = malloc(sizeof(sc->mfi_commands[0]) *
80608c89430SSteven Hartland 	    sc->mfi_max_fw_cmds, M_MFIBUF, M_WAITOK | M_ZERO);
8070929b669SScott Long 
80808c89430SSteven Hartland 	for (i = 0; i < sc->mfi_max_fw_cmds; i++) {
8092e21a3efSScott Long 		cm = &sc->mfi_commands[i];
8102e21a3efSScott Long 		cm->cm_frame = (union mfi_frame *)((uintptr_t)sc->mfi_frames +
81178e36c27SScott Long 		    sc->mfi_cmd_size * i);
8122e21a3efSScott Long 		cm->cm_frame_busaddr = sc->mfi_frames_busaddr +
81378e36c27SScott Long 		    sc->mfi_cmd_size * i;
8142e21a3efSScott Long 		cm->cm_frame->header.context = i;
8152e21a3efSScott Long 		cm->cm_sense = &sc->mfi_sense[i];
8162e21a3efSScott Long 		cm->cm_sense_busaddr= sc->mfi_sense_busaddr + MFI_SENSE_LEN * i;
8172e21a3efSScott Long 		cm->cm_sc = sc;
8185ba21ff1SScott Long 		cm->cm_index = i;
8192e21a3efSScott Long 		if (bus_dmamap_create(sc->mfi_buffer_dmat, 0,
8200d9a4ef3SDoug Ambrisko 		    &cm->cm_dmamap) == 0) {
8210d9a4ef3SDoug Ambrisko 			mtx_lock(&sc->mfi_io_lock);
8222e21a3efSScott Long 			mfi_release_command(cm);
8230d9a4ef3SDoug Ambrisko 			mtx_unlock(&sc->mfi_io_lock);
82408c89430SSteven Hartland 		} else {
82508c89430SSteven Hartland 			device_printf(sc->mfi_dev, "Failed to allocate %d "
82608c89430SSteven Hartland 			   "command blocks, only allocated %d\n",
82708c89430SSteven Hartland 			    sc->mfi_max_fw_cmds, i - 1);
82808c89430SSteven Hartland 			for (j = 0; j < i; j++) {
82908c89430SSteven Hartland 				cm = &sc->mfi_commands[i];
83008c89430SSteven Hartland 				bus_dmamap_destroy(sc->mfi_buffer_dmat,
83108c89430SSteven Hartland 				    cm->cm_dmamap);
8320d9a4ef3SDoug Ambrisko 			}
83308c89430SSteven Hartland 			free(sc->mfi_commands, M_MFIBUF);
83408c89430SSteven Hartland 			sc->mfi_commands = NULL;
83508c89430SSteven Hartland 
83608c89430SSteven Hartland 			return (ENOMEM);
83708c89430SSteven Hartland 		}
8382e21a3efSScott Long 	}
8392e21a3efSScott Long 
8402e21a3efSScott Long 	return (0);
8412e21a3efSScott Long }
8422e21a3efSScott Long 
84335ef86f2SScott Long void
mfi_release_command(struct mfi_command * cm)8442e21a3efSScott Long mfi_release_command(struct mfi_command *cm)
8452e21a3efSScott Long {
846441f6d5dSScott Long 	struct mfi_frame_header *hdr;
8472e21a3efSScott Long 	uint32_t *hdr_data;
8482e21a3efSScott Long 
8490d9a4ef3SDoug Ambrisko 	mtx_assert(&cm->cm_sc->mfi_io_lock, MA_OWNED);
8500d9a4ef3SDoug Ambrisko 
8512e21a3efSScott Long 	/*
8522e21a3efSScott Long 	 * Zero out the important fields of the frame, but make sure the
853a91d6dabSScott Long 	 * context field is preserved.  For efficiency, handle the fields
854a91d6dabSScott Long 	 * as 32 bit words.  Clear out the first S/G entry too for safety.
8552e21a3efSScott Long 	 */
856441f6d5dSScott Long 	hdr = &cm->cm_frame->header;
85704697de9SDoug Ambrisko 	if (cm->cm_data != NULL && hdr->sg_count) {
858441f6d5dSScott Long 		cm->cm_sg->sg32[0].len = 0;
859441f6d5dSScott Long 		cm->cm_sg->sg32[0].addr = 0;
860441f6d5dSScott Long 	}
861a91d6dabSScott Long 
86208c89430SSteven Hartland 	/*
86308c89430SSteven Hartland 	 * Command may be on other queues e.g. busy queue depending on the
86408c89430SSteven Hartland 	 * flow of a previous call to mfi_mapcmd, so ensure its dequeued
86508c89430SSteven Hartland 	 * properly
86608c89430SSteven Hartland 	 */
86708c89430SSteven Hartland 	if ((cm->cm_flags & MFI_ON_MFIQ_BUSY) != 0)
86808c89430SSteven Hartland 		mfi_remove_busy(cm);
86908c89430SSteven Hartland 	if ((cm->cm_flags & MFI_ON_MFIQ_READY) != 0)
87008c89430SSteven Hartland 		mfi_remove_ready(cm);
87108c89430SSteven Hartland 
87208c89430SSteven Hartland 	/* We're not expecting it to be on any other queue but check */
87308c89430SSteven Hartland 	if ((cm->cm_flags & MFI_ON_MFIQ_MASK) != 0) {
87408c89430SSteven Hartland 		panic("Command %p is still on another queue, flags = %#x",
87508c89430SSteven Hartland 		    cm, cm->cm_flags);
87608c89430SSteven Hartland 	}
87708c89430SSteven Hartland 
87808c89430SSteven Hartland 	/* tbolt cleanup */
87908c89430SSteven Hartland 	if ((cm->cm_flags & MFI_CMD_TBOLT) != 0) {
88008c89430SSteven Hartland 		mfi_tbolt_return_cmd(cm->cm_sc,
88108c89430SSteven Hartland 		    cm->cm_sc->mfi_cmd_pool_tbolt[cm->cm_extra_frames - 1],
88208c89430SSteven Hartland 		    cm);
88308c89430SSteven Hartland 	}
88408c89430SSteven Hartland 
885a91d6dabSScott Long 	hdr_data = (uint32_t *)cm->cm_frame;
886a91d6dabSScott Long 	hdr_data[0] = 0;	/* cmd, sense_len, cmd_status, scsi_status */
887a91d6dabSScott Long 	hdr_data[1] = 0;	/* target_id, lun_id, cdb_len, sg_count */
888a91d6dabSScott Long 	hdr_data[4] = 0;	/* flags, timeout */
889a91d6dabSScott Long 	hdr_data[5] = 0;	/* data_len */
890a91d6dabSScott Long 
8912e21a3efSScott Long 	cm->cm_extra_frames = 0;
8922e21a3efSScott Long 	cm->cm_flags = 0;
8932e21a3efSScott Long 	cm->cm_complete = NULL;
8942e21a3efSScott Long 	cm->cm_private = NULL;
89535ef86f2SScott Long 	cm->cm_data = NULL;
8962e21a3efSScott Long 	cm->cm_sg = 0;
8972e21a3efSScott Long 	cm->cm_total_frame_size = 0;
8980d9a4ef3SDoug Ambrisko 	cm->retry_for_fw_reset = 0;
899441f6d5dSScott Long 
9002e21a3efSScott Long 	mfi_enqueue_free(cm);
9012e21a3efSScott Long }
9022e21a3efSScott Long 
903ddbffe7fSDoug Ambrisko int
mfi_dcmd_command(struct mfi_softc * sc,struct mfi_command ** cmp,uint32_t opcode,void ** bufp,size_t bufsize)904a6ba0fd6SDoug Ambrisko mfi_dcmd_command(struct mfi_softc *sc, struct mfi_command **cmp,
905a6ba0fd6SDoug Ambrisko     uint32_t opcode, void **bufp, size_t bufsize)
906fb595e7aSPaul Saab {
907fb595e7aSPaul Saab 	struct mfi_command *cm;
908fb595e7aSPaul Saab 	struct mfi_dcmd_frame *dcmd;
909fb595e7aSPaul Saab 	void *buf = NULL;
9100d9a4ef3SDoug Ambrisko 	uint32_t context = 0;
911fb595e7aSPaul Saab 
912fb595e7aSPaul Saab 	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
913fb595e7aSPaul Saab 
914fb595e7aSPaul Saab 	cm = mfi_dequeue_free(sc);
915fb595e7aSPaul Saab 	if (cm == NULL)
916fb595e7aSPaul Saab 		return (EBUSY);
917fb595e7aSPaul Saab 
9180d9a4ef3SDoug Ambrisko 	/* Zero out the MFI frame */
9190d9a4ef3SDoug Ambrisko 	context = cm->cm_frame->header.context;
9200d9a4ef3SDoug Ambrisko 	bzero(cm->cm_frame, sizeof(union mfi_frame));
9210d9a4ef3SDoug Ambrisko 	cm->cm_frame->header.context = context;
9220d9a4ef3SDoug Ambrisko 
923fb595e7aSPaul Saab 	if ((bufsize > 0) && (bufp != NULL)) {
924fb595e7aSPaul Saab 		if (*bufp == NULL) {
925fb595e7aSPaul Saab 			buf = malloc(bufsize, M_MFIBUF, M_NOWAIT|M_ZERO);
926fb595e7aSPaul Saab 			if (buf == NULL) {
927fb595e7aSPaul Saab 				mfi_release_command(cm);
928fb595e7aSPaul Saab 				return (ENOMEM);
929fb595e7aSPaul Saab 			}
930fb595e7aSPaul Saab 			*bufp = buf;
931fb595e7aSPaul Saab 		} else {
932fb595e7aSPaul Saab 			buf = *bufp;
933fb595e7aSPaul Saab 		}
934fb595e7aSPaul Saab 	}
935fb595e7aSPaul Saab 
936fb595e7aSPaul Saab 	dcmd =  &cm->cm_frame->dcmd;
937fb595e7aSPaul Saab 	bzero(dcmd->mbox, MFI_MBOX_SIZE);
938fb595e7aSPaul Saab 	dcmd->header.cmd = MFI_CMD_DCMD;
939fb595e7aSPaul Saab 	dcmd->header.timeout = 0;
940fb595e7aSPaul Saab 	dcmd->header.flags = 0;
941fb595e7aSPaul Saab 	dcmd->header.data_len = bufsize;
9420d9a4ef3SDoug Ambrisko 	dcmd->header.scsi_status = 0;
943fb595e7aSPaul Saab 	dcmd->opcode = opcode;
944fb595e7aSPaul Saab 	cm->cm_sg = &dcmd->sgl;
945fb595e7aSPaul Saab 	cm->cm_total_frame_size = MFI_DCMD_FRAME_SIZE;
946fb595e7aSPaul Saab 	cm->cm_flags = 0;
947fb595e7aSPaul Saab 	cm->cm_data = buf;
948fb595e7aSPaul Saab 	cm->cm_private = buf;
949fb595e7aSPaul Saab 	cm->cm_len = bufsize;
950fb595e7aSPaul Saab 
951fb595e7aSPaul Saab 	*cmp = cm;
952fb595e7aSPaul Saab 	if ((bufp != NULL) && (*bufp == NULL) && (buf != NULL))
953fb595e7aSPaul Saab 		*bufp = buf;
954fb595e7aSPaul Saab 	return (0);
955fb595e7aSPaul Saab }
956fb595e7aSPaul Saab 
957fb595e7aSPaul Saab static int
mfi_comms_init(struct mfi_softc * sc)9582e21a3efSScott Long mfi_comms_init(struct mfi_softc *sc)
9592e21a3efSScott Long {
9602e21a3efSScott Long 	struct mfi_command *cm;
9612e21a3efSScott Long 	struct mfi_init_frame *init;
9622e21a3efSScott Long 	struct mfi_init_qinfo *qinfo;
9632e21a3efSScott Long 	int error;
9640d9a4ef3SDoug Ambrisko 	uint32_t context = 0;
9652e21a3efSScott Long 
966441f6d5dSScott Long 	mtx_lock(&sc->mfi_io_lock);
96708c89430SSteven Hartland 	if ((cm = mfi_dequeue_free(sc)) == NULL) {
96808c89430SSteven Hartland 		mtx_unlock(&sc->mfi_io_lock);
9692e21a3efSScott Long 		return (EBUSY);
97008c89430SSteven Hartland 	}
9712e21a3efSScott Long 
9720d9a4ef3SDoug Ambrisko 	/* Zero out the MFI frame */
9730d9a4ef3SDoug Ambrisko 	context = cm->cm_frame->header.context;
9740d9a4ef3SDoug Ambrisko 	bzero(cm->cm_frame, sizeof(union mfi_frame));
9750d9a4ef3SDoug Ambrisko 	cm->cm_frame->header.context = context;
9760d9a4ef3SDoug Ambrisko 
9772e21a3efSScott Long 	/*
9782e21a3efSScott Long 	 * Abuse the SG list area of the frame to hold the init_qinfo
9792e21a3efSScott Long 	 * object;
9802e21a3efSScott Long 	 */
9812e21a3efSScott Long 	init = &cm->cm_frame->init;
9822e21a3efSScott Long 	qinfo = (struct mfi_init_qinfo *)((uintptr_t)init + MFI_FRAME_SIZE);
9832e21a3efSScott Long 
9842e21a3efSScott Long 	bzero(qinfo, sizeof(struct mfi_init_qinfo));
9852e21a3efSScott Long 	qinfo->rq_entries = sc->mfi_max_fw_cmds + 1;
9862e21a3efSScott Long 	qinfo->rq_addr_lo = sc->mfi_comms_busaddr +
9872e21a3efSScott Long 	    offsetof(struct mfi_hwcomms, hw_reply_q);
9882e21a3efSScott Long 	qinfo->pi_addr_lo = sc->mfi_comms_busaddr +
9892e21a3efSScott Long 	    offsetof(struct mfi_hwcomms, hw_pi);
9902e21a3efSScott Long 	qinfo->ci_addr_lo = sc->mfi_comms_busaddr +
9912e21a3efSScott Long 	    offsetof(struct mfi_hwcomms, hw_ci);
9922e21a3efSScott Long 
9932e21a3efSScott Long 	init->header.cmd = MFI_CMD_INIT;
9942e21a3efSScott Long 	init->header.data_len = sizeof(struct mfi_init_qinfo);
9952e21a3efSScott Long 	init->qinfo_new_addr_lo = cm->cm_frame_busaddr + MFI_FRAME_SIZE;
9963c77cee1SScott Long 	cm->cm_data = NULL;
9973c77cee1SScott Long 	cm->cm_flags = MFI_CMD_POLLED;
9982e21a3efSScott Long 
99908c89430SSteven Hartland 	if ((error = mfi_mapcmd(sc, cm)) != 0)
10002e21a3efSScott Long 		device_printf(sc->mfi_dev, "failed to send init command\n");
10012e21a3efSScott Long 	mfi_release_command(cm);
1002441f6d5dSScott Long 	mtx_unlock(&sc->mfi_io_lock);
10032e21a3efSScott Long 
100408c89430SSteven Hartland 	return (error);
10052e21a3efSScott Long }
10062e21a3efSScott Long 
10072e21a3efSScott Long static int
mfi_get_controller_info(struct mfi_softc * sc)10082e21a3efSScott Long mfi_get_controller_info(struct mfi_softc *sc)
10092e21a3efSScott Long {
1010fb595e7aSPaul Saab 	struct mfi_command *cm = NULL;
1011fb595e7aSPaul Saab 	struct mfi_ctrl_info *ci = NULL;
10122e21a3efSScott Long 	uint32_t max_sectors_1, max_sectors_2;
10132e21a3efSScott Long 	int error;
10142e21a3efSScott Long 
1015fb595e7aSPaul Saab 	mtx_lock(&sc->mfi_io_lock);
1016fb595e7aSPaul Saab 	error = mfi_dcmd_command(sc, &cm, MFI_DCMD_CTRL_GETINFO,
1017fb595e7aSPaul Saab 	    (void **)&ci, sizeof(*ci));
1018fb595e7aSPaul Saab 	if (error)
1019fb595e7aSPaul Saab 		goto out;
10202e21a3efSScott Long 	cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_POLLED;
10212e21a3efSScott Long 
10222e21a3efSScott Long 	if ((error = mfi_mapcmd(sc, cm)) != 0) {
10232e21a3efSScott Long 		device_printf(sc->mfi_dev, "Failed to get controller info\n");
102478e36c27SScott Long 		sc->mfi_max_io = (sc->mfi_max_sge - 1) * PAGE_SIZE /
10252e21a3efSScott Long 		    MFI_SECTOR_LEN;
1026fb595e7aSPaul Saab 		error = 0;
1027fb595e7aSPaul Saab 		goto out;
10282e21a3efSScott Long 	}
10292e21a3efSScott Long 
10302e21a3efSScott Long 	bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap,
10312e21a3efSScott Long 	    BUS_DMASYNC_POSTREAD);
10322e21a3efSScott Long 	bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap);
10332e21a3efSScott Long 
10340d9a4ef3SDoug Ambrisko 	max_sectors_1 = (1 << ci->stripe_sz_ops.max) * ci->max_strips_per_io;
10352e21a3efSScott Long 	max_sectors_2 = ci->max_request_size;
10362e21a3efSScott Long 	sc->mfi_max_io = min(max_sectors_1, max_sectors_2);
1037a6ba0fd6SDoug Ambrisko 	sc->disableOnlineCtrlReset =
1038a6ba0fd6SDoug Ambrisko 	    ci->properties.OnOffProperties.disableOnlineCtrlReset;
10392e21a3efSScott Long 
1040fb595e7aSPaul Saab out:
1041fb595e7aSPaul Saab 	if (ci)
10422e21a3efSScott Long 		free(ci, M_MFIBUF);
1043fb595e7aSPaul Saab 	if (cm)
10442e21a3efSScott Long 		mfi_release_command(cm);
1045fb595e7aSPaul Saab 	mtx_unlock(&sc->mfi_io_lock);
10462e21a3efSScott Long 	return (error);
10472e21a3efSScott Long }
10482e21a3efSScott Long 
10492e21a3efSScott Long static int
mfi_get_log_state(struct mfi_softc * sc,struct mfi_evt_log_state ** log_state)1050fb595e7aSPaul Saab mfi_get_log_state(struct mfi_softc *sc, struct mfi_evt_log_state **log_state)
1051741367d5SDoug Ambrisko {
1052e94bb9b2SPaul Saab 	struct mfi_command *cm = NULL;
1053741367d5SDoug Ambrisko 	int error;
1054741367d5SDoug Ambrisko 
105508c89430SSteven Hartland 	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
1056fb595e7aSPaul Saab 	error = mfi_dcmd_command(sc, &cm, MFI_DCMD_CTRL_EVENT_GETINFO,
1057fb595e7aSPaul Saab 	    (void **)log_state, sizeof(**log_state));
1058fb595e7aSPaul Saab 	if (error)
1059fb595e7aSPaul Saab 		goto out;
1060330df1acSPaul Saab 	cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_POLLED;
1061741367d5SDoug Ambrisko 
1062741367d5SDoug Ambrisko 	if ((error = mfi_mapcmd(sc, cm)) != 0) {
106383ff9c13SPaul Saab 		device_printf(sc->mfi_dev, "Failed to get log state\n");
1064fb595e7aSPaul Saab 		goto out;
1065741367d5SDoug Ambrisko 	}
1066741367d5SDoug Ambrisko 
1067741367d5SDoug Ambrisko 	bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap,
1068741367d5SDoug Ambrisko 	    BUS_DMASYNC_POSTREAD);
1069741367d5SDoug Ambrisko 	bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap);
1070741367d5SDoug Ambrisko 
1071fb595e7aSPaul Saab out:
1072e94bb9b2SPaul Saab 	if (cm)
1073741367d5SDoug Ambrisko 		mfi_release_command(cm);
1074741367d5SDoug Ambrisko 
1075741367d5SDoug Ambrisko 	return (error);
1076741367d5SDoug Ambrisko }
1077741367d5SDoug Ambrisko 
10780d9a4ef3SDoug Ambrisko int
mfi_aen_setup(struct mfi_softc * sc,uint32_t seq_start)1079741367d5SDoug Ambrisko mfi_aen_setup(struct mfi_softc *sc, uint32_t seq_start)
1080741367d5SDoug Ambrisko {
1081fb595e7aSPaul Saab 	struct mfi_evt_log_state *log_state = NULL;
1082741367d5SDoug Ambrisko 	union mfi_evt class_locale;
1083741367d5SDoug Ambrisko 	int error = 0;
1084741367d5SDoug Ambrisko 	uint32_t seq;
1085741367d5SDoug Ambrisko 
108608c89430SSteven Hartland 	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
108708c89430SSteven Hartland 
1088741367d5SDoug Ambrisko 	class_locale.members.reserved = 0;
108947b470b9SDoug Ambrisko 	class_locale.members.locale = mfi_event_locale;
1090aacea6e2SEd Maste 	class_locale.members.evt_class  = mfi_event_class;
1091741367d5SDoug Ambrisko 
1092741367d5SDoug Ambrisko 	if (seq_start == 0) {
109308c89430SSteven Hartland 		if ((error = mfi_get_log_state(sc, &log_state)) != 0)
109408c89430SSteven Hartland 			goto out;
1095a6ba0fd6SDoug Ambrisko 		sc->mfi_boot_seq_num = log_state->boot_seq_num;
1096c1ed06a8SJohn Baldwin 
1097c1ed06a8SJohn Baldwin 		/*
1098c1ed06a8SJohn Baldwin 		 * Walk through any events that fired since the last
1099c1ed06a8SJohn Baldwin 		 * shutdown.
1100c1ed06a8SJohn Baldwin 		 */
110108c89430SSteven Hartland 		if ((error = mfi_parse_entries(sc, log_state->shutdown_seq_num,
110208c89430SSteven Hartland 		    log_state->newest_seq_num)) != 0)
110308c89430SSteven Hartland 			goto out;
1104c1ed06a8SJohn Baldwin 		seq = log_state->newest_seq_num;
1105741367d5SDoug Ambrisko 	} else
1106741367d5SDoug Ambrisko 		seq = seq_start;
110708c89430SSteven Hartland 	error = mfi_aen_register(sc, seq, class_locale.word);
110808c89430SSteven Hartland out:
1109fb595e7aSPaul Saab 	free(log_state, M_MFIBUF);
1110741367d5SDoug Ambrisko 
111108c89430SSteven Hartland 	return (error);
1112741367d5SDoug Ambrisko }
1113741367d5SDoug Ambrisko 
11140d9a4ef3SDoug Ambrisko int
mfi_wait_command(struct mfi_softc * sc,struct mfi_command * cm)1115c0b332d1SPaul Saab mfi_wait_command(struct mfi_softc *sc, struct mfi_command *cm)
1116c0b332d1SPaul Saab {
1117c0b332d1SPaul Saab 
1118c0b332d1SPaul Saab 	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
1119c0b332d1SPaul Saab 	cm->cm_complete = NULL;
1120c0b332d1SPaul Saab 
11215be25877SDoug Ambrisko 	/*
11225be25877SDoug Ambrisko 	 * MegaCli can issue a DCMD of 0.  In this case do nothing
11235be25877SDoug Ambrisko 	 * and return 0 to it as status
11245be25877SDoug Ambrisko 	 */
11255be25877SDoug Ambrisko 	if (cm->cm_frame->dcmd.opcode == 0) {
11265be25877SDoug Ambrisko 		cm->cm_frame->header.cmd_status = MFI_STAT_OK;
11275be25877SDoug Ambrisko 		cm->cm_error = 0;
11285be25877SDoug Ambrisko 		return (cm->cm_error);
11295be25877SDoug Ambrisko 	}
1130c0b332d1SPaul Saab 	mfi_enqueue_ready(cm);
1131c0b332d1SPaul Saab 	mfi_startio(sc);
11325be25877SDoug Ambrisko 	if ((cm->cm_flags & MFI_CMD_COMPLETED) == 0)
11335be25877SDoug Ambrisko 		msleep(cm, &sc->mfi_io_lock, PRIBIO, "mfiwait", 0);
11345be25877SDoug Ambrisko 	return (cm->cm_error);
1135c0b332d1SPaul Saab }
1136c0b332d1SPaul Saab 
11372e21a3efSScott Long void
mfi_free(struct mfi_softc * sc)11382e21a3efSScott Long mfi_free(struct mfi_softc *sc)
11392e21a3efSScott Long {
11402e21a3efSScott Long 	struct mfi_command *cm;
11412e21a3efSScott Long 	int i;
11422e21a3efSScott Long 
11435ba21ff1SScott Long 	callout_drain(&sc->mfi_watchdog_callout);
11445ba21ff1SScott Long 
11452e21a3efSScott Long 	if (sc->mfi_cdev != NULL)
11462e21a3efSScott Long 		destroy_dev(sc->mfi_cdev);
11472e21a3efSScott Long 
114808c89430SSteven Hartland 	if (sc->mfi_commands != NULL) {
114908c89430SSteven Hartland 		for (i = 0; i < sc->mfi_max_fw_cmds; i++) {
11502e21a3efSScott Long 			cm = &sc->mfi_commands[i];
11512e21a3efSScott Long 			bus_dmamap_destroy(sc->mfi_buffer_dmat, cm->cm_dmamap);
11522e21a3efSScott Long 		}
11532e21a3efSScott Long 		free(sc->mfi_commands, M_MFIBUF);
115408c89430SSteven Hartland 		sc->mfi_commands = NULL;
11552e21a3efSScott Long 	}
11562e21a3efSScott Long 
11572e21a3efSScott Long 	if (sc->mfi_intr)
11582e21a3efSScott Long 		bus_teardown_intr(sc->mfi_dev, sc->mfi_irq, sc->mfi_intr);
11592e21a3efSScott Long 	if (sc->mfi_irq != NULL)
11602e21a3efSScott Long 		bus_release_resource(sc->mfi_dev, SYS_RES_IRQ, sc->mfi_irq_rid,
11612e21a3efSScott Long 		    sc->mfi_irq);
11622e21a3efSScott Long 
11632e21a3efSScott Long 	if (sc->mfi_sense_busaddr != 0)
11642e21a3efSScott Long 		bus_dmamap_unload(sc->mfi_sense_dmat, sc->mfi_sense_dmamap);
11652e21a3efSScott Long 	if (sc->mfi_sense != NULL)
11662e21a3efSScott Long 		bus_dmamem_free(sc->mfi_sense_dmat, sc->mfi_sense,
11672e21a3efSScott Long 		    sc->mfi_sense_dmamap);
11682e21a3efSScott Long 	if (sc->mfi_sense_dmat != NULL)
11692e21a3efSScott Long 		bus_dma_tag_destroy(sc->mfi_sense_dmat);
11702e21a3efSScott Long 
11712e21a3efSScott Long 	if (sc->mfi_frames_busaddr != 0)
11722e21a3efSScott Long 		bus_dmamap_unload(sc->mfi_frames_dmat, sc->mfi_frames_dmamap);
11732e21a3efSScott Long 	if (sc->mfi_frames != NULL)
11742e21a3efSScott Long 		bus_dmamem_free(sc->mfi_frames_dmat, sc->mfi_frames,
11752e21a3efSScott Long 		    sc->mfi_frames_dmamap);
11762e21a3efSScott Long 	if (sc->mfi_frames_dmat != NULL)
11772e21a3efSScott Long 		bus_dma_tag_destroy(sc->mfi_frames_dmat);
11782e21a3efSScott Long 
11792e21a3efSScott Long 	if (sc->mfi_comms_busaddr != 0)
11802e21a3efSScott Long 		bus_dmamap_unload(sc->mfi_comms_dmat, sc->mfi_comms_dmamap);
11812e21a3efSScott Long 	if (sc->mfi_comms != NULL)
11822e21a3efSScott Long 		bus_dmamem_free(sc->mfi_comms_dmat, sc->mfi_comms,
11832e21a3efSScott Long 		    sc->mfi_comms_dmamap);
11842e21a3efSScott Long 	if (sc->mfi_comms_dmat != NULL)
11852e21a3efSScott Long 		bus_dma_tag_destroy(sc->mfi_comms_dmat);
11862e21a3efSScott Long 
11870d9a4ef3SDoug Ambrisko 	/* ThunderBolt contiguous memory free here */
1188a6ba0fd6SDoug Ambrisko 	if (sc->mfi_flags & MFI_FLAGS_TBOLT) {
11890d9a4ef3SDoug Ambrisko 		if (sc->mfi_tb_busaddr != 0)
11900d9a4ef3SDoug Ambrisko 			bus_dmamap_unload(sc->mfi_tb_dmat, sc->mfi_tb_dmamap);
11910d9a4ef3SDoug Ambrisko 		if (sc->request_message_pool != NULL)
11920d9a4ef3SDoug Ambrisko 			bus_dmamem_free(sc->mfi_tb_dmat, sc->request_message_pool,
11930d9a4ef3SDoug Ambrisko 			    sc->mfi_tb_dmamap);
11940d9a4ef3SDoug Ambrisko 		if (sc->mfi_tb_dmat != NULL)
11950d9a4ef3SDoug Ambrisko 			bus_dma_tag_destroy(sc->mfi_tb_dmat);
11960d9a4ef3SDoug Ambrisko 
11970d9a4ef3SDoug Ambrisko 		/* Version buffer memory free */
1198a6ba0fd6SDoug Ambrisko 		/* Start LSIP200113393 */
11990d9a4ef3SDoug Ambrisko 		if (sc->verbuf_h_busaddr != 0)
12000d9a4ef3SDoug Ambrisko 			bus_dmamap_unload(sc->verbuf_h_dmat, sc->verbuf_h_dmamap);
12010d9a4ef3SDoug Ambrisko 		if (sc->verbuf != NULL)
12020d9a4ef3SDoug Ambrisko 			bus_dmamem_free(sc->verbuf_h_dmat, sc->verbuf,
12030d9a4ef3SDoug Ambrisko 			    sc->verbuf_h_dmamap);
12040d9a4ef3SDoug Ambrisko 		if (sc->verbuf_h_dmat != NULL)
12050d9a4ef3SDoug Ambrisko 			bus_dma_tag_destroy(sc->verbuf_h_dmat);
12060d9a4ef3SDoug Ambrisko 
1207a6ba0fd6SDoug Ambrisko 		/* End LSIP200113393 */
1208a6ba0fd6SDoug Ambrisko 		/* ThunderBolt INIT packet memory Free */
12090d9a4ef3SDoug Ambrisko 		if (sc->mfi_tb_init_busaddr != 0)
121008c89430SSteven Hartland 			bus_dmamap_unload(sc->mfi_tb_init_dmat,
121108c89430SSteven Hartland 			    sc->mfi_tb_init_dmamap);
12120d9a4ef3SDoug Ambrisko 		if (sc->mfi_tb_init != NULL)
12130d9a4ef3SDoug Ambrisko 			bus_dmamem_free(sc->mfi_tb_init_dmat, sc->mfi_tb_init,
12140d9a4ef3SDoug Ambrisko 			    sc->mfi_tb_init_dmamap);
12150d9a4ef3SDoug Ambrisko 		if (sc->mfi_tb_init_dmat != NULL)
12160d9a4ef3SDoug Ambrisko 			bus_dma_tag_destroy(sc->mfi_tb_init_dmat);
12170d9a4ef3SDoug Ambrisko 
12180d9a4ef3SDoug Ambrisko 		/* ThunderBolt IOC Init Desc memory free here */
12190d9a4ef3SDoug Ambrisko 		if (sc->mfi_tb_ioc_init_busaddr != 0)
1220a6ba0fd6SDoug Ambrisko 			bus_dmamap_unload(sc->mfi_tb_ioc_init_dmat,
1221a6ba0fd6SDoug Ambrisko 			    sc->mfi_tb_ioc_init_dmamap);
12220d9a4ef3SDoug Ambrisko 		if (sc->mfi_tb_ioc_init_desc != NULL)
1223a6ba0fd6SDoug Ambrisko 			bus_dmamem_free(sc->mfi_tb_ioc_init_dmat,
1224a6ba0fd6SDoug Ambrisko 			    sc->mfi_tb_ioc_init_desc,
1225a6ba0fd6SDoug Ambrisko 			    sc->mfi_tb_ioc_init_dmamap);
12260d9a4ef3SDoug Ambrisko 		if (sc->mfi_tb_ioc_init_dmat != NULL)
12270d9a4ef3SDoug Ambrisko 			bus_dma_tag_destroy(sc->mfi_tb_ioc_init_dmat);
1228a6ba0fd6SDoug Ambrisko 		if (sc->mfi_cmd_pool_tbolt != NULL) {
122908c89430SSteven Hartland 			for (int i = 0; i < sc->mfi_max_fw_cmds; i++) {
1230a6ba0fd6SDoug Ambrisko 				if (sc->mfi_cmd_pool_tbolt[i] != NULL) {
1231a6ba0fd6SDoug Ambrisko 					free(sc->mfi_cmd_pool_tbolt[i],
1232a6ba0fd6SDoug Ambrisko 					    M_MFIBUF);
12330d9a4ef3SDoug Ambrisko 					sc->mfi_cmd_pool_tbolt[i] = NULL;
12340d9a4ef3SDoug Ambrisko 				}
12350d9a4ef3SDoug Ambrisko 			}
12360d9a4ef3SDoug Ambrisko 			free(sc->mfi_cmd_pool_tbolt, M_MFIBUF);
12370d9a4ef3SDoug Ambrisko 			sc->mfi_cmd_pool_tbolt = NULL;
12380d9a4ef3SDoug Ambrisko 		}
1239a6ba0fd6SDoug Ambrisko 		if (sc->request_desc_pool != NULL) {
12400d9a4ef3SDoug Ambrisko 			free(sc->request_desc_pool, M_MFIBUF);
12410d9a4ef3SDoug Ambrisko 			sc->request_desc_pool = NULL;
12420d9a4ef3SDoug Ambrisko 		}
12430d9a4ef3SDoug Ambrisko 	}
12442e21a3efSScott Long 	if (sc->mfi_buffer_dmat != NULL)
12452e21a3efSScott Long 		bus_dma_tag_destroy(sc->mfi_buffer_dmat);
12462e21a3efSScott Long 	if (sc->mfi_parent_dmat != NULL)
12472e21a3efSScott Long 		bus_dma_tag_destroy(sc->mfi_parent_dmat);
12482e21a3efSScott Long 
12498ec5c98bSJohn Baldwin 	if (mtx_initialized(&sc->mfi_io_lock)) {
12502e21a3efSScott Long 		mtx_destroy(&sc->mfi_io_lock);
12518ec5c98bSJohn Baldwin 		sx_destroy(&sc->mfi_config_lock);
12528ec5c98bSJohn Baldwin 	}
12532e21a3efSScott Long 
12542e21a3efSScott Long 	return;
12552e21a3efSScott Long }
12562e21a3efSScott Long 
12572e21a3efSScott Long static void
mfi_startup(void * arg)12582e21a3efSScott Long mfi_startup(void *arg)
12592e21a3efSScott Long {
12602e21a3efSScott Long 	struct mfi_softc *sc;
12612e21a3efSScott Long 
12622e21a3efSScott Long 	sc = (struct mfi_softc *)arg;
12632e21a3efSScott Long 
1264610f2ef3SScott Long 	sc->mfi_enable_intr(sc);
12658ec5c98bSJohn Baldwin 	sx_xlock(&sc->mfi_config_lock);
1266441f6d5dSScott Long 	mtx_lock(&sc->mfi_io_lock);
1267c0b332d1SPaul Saab 	mfi_ldprobe(sc);
12680d9a4ef3SDoug Ambrisko 	if (sc->mfi_flags & MFI_FLAGS_SKINNY)
12690d9a4ef3SDoug Ambrisko 	    mfi_syspdprobe(sc);
1270441f6d5dSScott Long 	mtx_unlock(&sc->mfi_io_lock);
12718ec5c98bSJohn Baldwin 	sx_xunlock(&sc->mfi_config_lock);
12723e0ca40aSScott Long 
12733e0ca40aSScott Long 	config_intrhook_disestablish(&sc->mfi_ich);
12742e21a3efSScott Long }
12752e21a3efSScott Long 
12762e21a3efSScott Long static void
mfi_intr(void * arg)12772e21a3efSScott Long mfi_intr(void *arg)
12782e21a3efSScott Long {
12792e21a3efSScott Long 	struct mfi_softc *sc;
12802e21a3efSScott Long 	struct mfi_command *cm;
1281610f2ef3SScott Long 	uint32_t pi, ci, context;
12822e21a3efSScott Long 
12832e21a3efSScott Long 	sc = (struct mfi_softc *)arg;
12842e21a3efSScott Long 
1285610f2ef3SScott Long 	if (sc->mfi_check_clear_intr(sc))
12862e21a3efSScott Long 		return;
1287441f6d5dSScott Long 
1288a6ba0fd6SDoug Ambrisko restart:
12892e21a3efSScott Long 	pi = sc->mfi_comms->hw_pi;
12902e21a3efSScott Long 	ci = sc->mfi_comms->hw_ci;
12912e21a3efSScott Long 	mtx_lock(&sc->mfi_io_lock);
12922e21a3efSScott Long 	while (ci != pi) {
12932e21a3efSScott Long 		context = sc->mfi_comms->hw_reply_q[ci];
12945be25877SDoug Ambrisko 		if (context < sc->mfi_max_fw_cmds) {
12952e21a3efSScott Long 			cm = &sc->mfi_commands[context];
12962e21a3efSScott Long 			mfi_remove_busy(cm);
12975be25877SDoug Ambrisko 			cm->cm_error = 0;
12982e21a3efSScott Long 			mfi_complete(sc, cm);
12995be25877SDoug Ambrisko 		}
130008c89430SSteven Hartland 		if (++ci == (sc->mfi_max_fw_cmds + 1))
13012e21a3efSScott Long 			ci = 0;
13022e21a3efSScott Long 	}
13032e21a3efSScott Long 
13042e21a3efSScott Long 	sc->mfi_comms->hw_ci = ci;
13052e21a3efSScott Long 
1306441f6d5dSScott Long 	/* Give defered I/O a chance to run */
1307441f6d5dSScott Long 	sc->mfi_flags &= ~MFI_FLAGS_QFRZN;
1308441f6d5dSScott Long 	mfi_startio(sc);
1309441f6d5dSScott Long 	mtx_unlock(&sc->mfi_io_lock);
1310441f6d5dSScott Long 
1311a6ba0fd6SDoug Ambrisko 	/*
1312a6ba0fd6SDoug Ambrisko 	 * Dummy read to flush the bus; this ensures that the indexes are up
1313a6ba0fd6SDoug Ambrisko 	 * to date.  Restart processing if more commands have come it.
1314a6ba0fd6SDoug Ambrisko 	 */
1315a6ba0fd6SDoug Ambrisko 	(void)sc->mfi_read_fw_status(sc);
1316a6ba0fd6SDoug Ambrisko 	if (pi != sc->mfi_comms->hw_pi)
1317a6ba0fd6SDoug Ambrisko 		goto restart;
1318a6ba0fd6SDoug Ambrisko 
13192e21a3efSScott Long 	return;
13202e21a3efSScott Long }
13212e21a3efSScott Long 
13222e21a3efSScott Long int
mfi_shutdown(struct mfi_softc * sc)13232e21a3efSScott Long mfi_shutdown(struct mfi_softc *sc)
13242e21a3efSScott Long {
13252e21a3efSScott Long 	struct mfi_dcmd_frame *dcmd;
13262e21a3efSScott Long 	struct mfi_command *cm;
13272e21a3efSScott Long 	int error;
13282e21a3efSScott Long 
132908c89430SSteven Hartland 	if (sc->mfi_aen_cm != NULL) {
133058ef3154SDoug Ambrisko 		sc->cm_aen_abort = 1;
133158ef3154SDoug Ambrisko 		mfi_abort(sc, &sc->mfi_aen_cm);
133208c89430SSteven Hartland 	}
133358ef3154SDoug Ambrisko 
133408c89430SSteven Hartland 	if (sc->mfi_map_sync_cm != NULL) {
133558ef3154SDoug Ambrisko 		sc->cm_map_abort = 1;
133658ef3154SDoug Ambrisko 		mfi_abort(sc, &sc->mfi_map_sync_cm);
133708c89430SSteven Hartland 	}
133858ef3154SDoug Ambrisko 
1339fb595e7aSPaul Saab 	mtx_lock(&sc->mfi_io_lock);
1340fb595e7aSPaul Saab 	error = mfi_dcmd_command(sc, &cm, MFI_DCMD_CTRL_SHUTDOWN, NULL, 0);
1341441f6d5dSScott Long 	if (error) {
1342fb595e7aSPaul Saab 		mtx_unlock(&sc->mfi_io_lock);
1343fb595e7aSPaul Saab 		return (error);
1344441f6d5dSScott Long 	}
13452e21a3efSScott Long 
13462e21a3efSScott Long 	dcmd = &cm->cm_frame->dcmd;
13472e21a3efSScott Long 	dcmd->header.flags = MFI_FRAME_DIR_NONE;
13483c77cee1SScott Long 	cm->cm_flags = MFI_CMD_POLLED;
13493c77cee1SScott Long 	cm->cm_data = NULL;
13502e21a3efSScott Long 
135108c89430SSteven Hartland 	if ((error = mfi_mapcmd(sc, cm)) != 0)
13522e21a3efSScott Long 		device_printf(sc->mfi_dev, "Failed to shutdown controller\n");
13532e21a3efSScott Long 
1354e94bb9b2SPaul Saab 	mfi_release_command(cm);
1355441f6d5dSScott Long 	mtx_unlock(&sc->mfi_io_lock);
13562e21a3efSScott Long 	return (error);
13572e21a3efSScott Long }
13582e21a3efSScott Long 
13592e21a3efSScott Long static void
mfi_syspdprobe(struct mfi_softc * sc)13600d9a4ef3SDoug Ambrisko mfi_syspdprobe(struct mfi_softc *sc)
13610d9a4ef3SDoug Ambrisko {
13620d9a4ef3SDoug Ambrisko 	struct mfi_frame_header *hdr;
13630d9a4ef3SDoug Ambrisko 	struct mfi_command *cm = NULL;
13640d9a4ef3SDoug Ambrisko 	struct mfi_pd_list *pdlist = NULL;
1365a6ba0fd6SDoug Ambrisko 	struct mfi_system_pd *syspd, *tmp;
136658ef3154SDoug Ambrisko 	struct mfi_system_pending *syspd_pend;
1367a6ba0fd6SDoug Ambrisko 	int error, i, found;
13680d9a4ef3SDoug Ambrisko 
13690d9a4ef3SDoug Ambrisko 	sx_assert(&sc->mfi_config_lock, SA_XLOCKED);
13700d9a4ef3SDoug Ambrisko 	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
13710d9a4ef3SDoug Ambrisko 	/* Add SYSTEM PD's */
13720d9a4ef3SDoug Ambrisko 	error = mfi_dcmd_command(sc, &cm, MFI_DCMD_PD_LIST_QUERY,
13730d9a4ef3SDoug Ambrisko 	    (void **)&pdlist, sizeof(*pdlist));
13740d9a4ef3SDoug Ambrisko 	if (error) {
1375a6ba0fd6SDoug Ambrisko 		device_printf(sc->mfi_dev,
1376a6ba0fd6SDoug Ambrisko 		    "Error while forming SYSTEM PD list\n");
13770d9a4ef3SDoug Ambrisko 		goto out;
13780d9a4ef3SDoug Ambrisko 	}
13790d9a4ef3SDoug Ambrisko 
13800d9a4ef3SDoug Ambrisko 	cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_POLLED;
13810d9a4ef3SDoug Ambrisko 	cm->cm_frame->dcmd.mbox[0] = MR_PD_QUERY_TYPE_EXPOSED_TO_HOST;
13820d9a4ef3SDoug Ambrisko 	cm->cm_frame->dcmd.mbox[1] = 0;
13830d9a4ef3SDoug Ambrisko 	if (mfi_mapcmd(sc, cm) != 0) {
1384a6ba0fd6SDoug Ambrisko 		device_printf(sc->mfi_dev,
1385a6ba0fd6SDoug Ambrisko 		    "Failed to get syspd device listing\n");
13860d9a4ef3SDoug Ambrisko 		goto out;
13870d9a4ef3SDoug Ambrisko 	}
13880d9a4ef3SDoug Ambrisko 	bus_dmamap_sync(sc->mfi_buffer_dmat,cm->cm_dmamap,
13890d9a4ef3SDoug Ambrisko 	    BUS_DMASYNC_POSTREAD);
13900d9a4ef3SDoug Ambrisko 	bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap);
13910d9a4ef3SDoug Ambrisko 	hdr = &cm->cm_frame->header;
13920d9a4ef3SDoug Ambrisko 	if (hdr->cmd_status != MFI_STAT_OK) {
1393a6ba0fd6SDoug Ambrisko 		device_printf(sc->mfi_dev,
1394a6ba0fd6SDoug Ambrisko 		    "MFI_DCMD_PD_LIST_QUERY failed %x\n", hdr->cmd_status);
13950d9a4ef3SDoug Ambrisko 		goto out;
13960d9a4ef3SDoug Ambrisko 	}
13970d9a4ef3SDoug Ambrisko 	/* Get each PD and add it to the system */
1398a6ba0fd6SDoug Ambrisko 	for (i = 0; i < pdlist->count; i++) {
1399a6ba0fd6SDoug Ambrisko 		if (pdlist->addr[i].device_id ==
1400a6ba0fd6SDoug Ambrisko 		    pdlist->addr[i].encl_device_id)
1401a6ba0fd6SDoug Ambrisko 			continue;
1402a6ba0fd6SDoug Ambrisko 		found = 0;
14030d9a4ef3SDoug Ambrisko 		TAILQ_FOREACH(syspd, &sc->mfi_syspd_tqh, pd_link) {
14040d9a4ef3SDoug Ambrisko 			if (syspd->pd_id == pdlist->addr[i].device_id)
1405a6ba0fd6SDoug Ambrisko 				found = 1;
14060d9a4ef3SDoug Ambrisko 		}
140758ef3154SDoug Ambrisko 		TAILQ_FOREACH(syspd_pend, &sc->mfi_syspd_pend_tqh, pd_link) {
140858ef3154SDoug Ambrisko 			if (syspd_pend->pd_id == pdlist->addr[i].device_id)
140958ef3154SDoug Ambrisko 				found = 1;
141058ef3154SDoug Ambrisko 		}
1411a6ba0fd6SDoug Ambrisko 		if (found == 0)
14120d9a4ef3SDoug Ambrisko 			mfi_add_sys_pd(sc, pdlist->addr[i].device_id);
14130d9a4ef3SDoug Ambrisko 	}
14140d9a4ef3SDoug Ambrisko 	/* Delete SYSPD's whose state has been changed */
1415a6ba0fd6SDoug Ambrisko 	TAILQ_FOREACH_SAFE(syspd, &sc->mfi_syspd_tqh, pd_link, tmp) {
1416a6ba0fd6SDoug Ambrisko 		found = 0;
14170d9a4ef3SDoug Ambrisko 		for (i = 0; i < pdlist->count; i++) {
141808c89430SSteven Hartland 			if (syspd->pd_id == pdlist->addr[i].device_id) {
1419a6ba0fd6SDoug Ambrisko 				found = 1;
142008c89430SSteven Hartland 				break;
142108c89430SSteven Hartland 			}
14220d9a4ef3SDoug Ambrisko 		}
1423a6ba0fd6SDoug Ambrisko 		if (found == 0) {
1424a6ba0fd6SDoug Ambrisko 			printf("DELETE\n");
1425a6ba0fd6SDoug Ambrisko 			mtx_unlock(&sc->mfi_io_lock);
1426c6df6f53SWarner Losh 			bus_topo_lock();
14270d9a4ef3SDoug Ambrisko 			device_delete_child(sc->mfi_dev, syspd->pd_dev);
1428c6df6f53SWarner Losh 			bus_topo_unlock();
1429a6ba0fd6SDoug Ambrisko 			mtx_lock(&sc->mfi_io_lock);
14300d9a4ef3SDoug Ambrisko 		}
14310d9a4ef3SDoug Ambrisko 	}
14320d9a4ef3SDoug Ambrisko out:
14330d9a4ef3SDoug Ambrisko 	if (pdlist)
14340d9a4ef3SDoug Ambrisko 	    free(pdlist, M_MFIBUF);
14350d9a4ef3SDoug Ambrisko 	if (cm)
14360d9a4ef3SDoug Ambrisko 	    mfi_release_command(cm);
14370d9a4ef3SDoug Ambrisko 
14380d9a4ef3SDoug Ambrisko 	return;
14390d9a4ef3SDoug Ambrisko }
14402e21a3efSScott Long 
14412e21a3efSScott Long static void
mfi_ldprobe(struct mfi_softc * sc)1442c0b332d1SPaul Saab mfi_ldprobe(struct mfi_softc *sc)
14432e21a3efSScott Long {
1444c0b332d1SPaul Saab 	struct mfi_frame_header *hdr;
1445c0b332d1SPaul Saab 	struct mfi_command *cm = NULL;
1446c0b332d1SPaul Saab 	struct mfi_ld_list *list = NULL;
14478ec5c98bSJohn Baldwin 	struct mfi_disk *ld;
144858ef3154SDoug Ambrisko 	struct mfi_disk_pending *ld_pend;
1449c0b332d1SPaul Saab 	int error, i;
14502e21a3efSScott Long 
14518ec5c98bSJohn Baldwin 	sx_assert(&sc->mfi_config_lock, SA_XLOCKED);
1452441f6d5dSScott Long 	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
1453441f6d5dSScott Long 
1454c0b332d1SPaul Saab 	error = mfi_dcmd_command(sc, &cm, MFI_DCMD_LD_GET_LIST,
1455c0b332d1SPaul Saab 	    (void **)&list, sizeof(*list));
1456c0b332d1SPaul Saab 	if (error)
1457c0b332d1SPaul Saab 		goto out;
1458c0b332d1SPaul Saab 
1459c0b332d1SPaul Saab 	cm->cm_flags = MFI_CMD_DATAIN;
1460c0b332d1SPaul Saab 	if (mfi_wait_command(sc, cm) != 0) {
1461c0b332d1SPaul Saab 		device_printf(sc->mfi_dev, "Failed to get device listing\n");
1462c0b332d1SPaul Saab 		goto out;
14632e21a3efSScott Long 	}
14642e21a3efSScott Long 
1465c0b332d1SPaul Saab 	hdr = &cm->cm_frame->header;
1466c0b332d1SPaul Saab 	if (hdr->cmd_status != MFI_STAT_OK) {
1467c0b332d1SPaul Saab 		device_printf(sc->mfi_dev, "MFI_DCMD_LD_GET_LIST failed %x\n",
1468c0b332d1SPaul Saab 		    hdr->cmd_status);
1469c0b332d1SPaul Saab 		goto out;
1470c0b332d1SPaul Saab 	}
1471c0b332d1SPaul Saab 
14728ec5c98bSJohn Baldwin 	for (i = 0; i < list->ld_count; i++) {
14738ec5c98bSJohn Baldwin 		TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link) {
14748ec5c98bSJohn Baldwin 			if (ld->ld_id == list->ld_list[i].ld.v.target_id)
14758ec5c98bSJohn Baldwin 				goto skip_add;
14768ec5c98bSJohn Baldwin 		}
147758ef3154SDoug Ambrisko 		TAILQ_FOREACH(ld_pend, &sc->mfi_ld_pend_tqh, ld_link) {
147858ef3154SDoug Ambrisko 			if (ld_pend->ld_id == list->ld_list[i].ld.v.target_id)
147958ef3154SDoug Ambrisko 				goto skip_add;
148058ef3154SDoug Ambrisko 		}
1481441f6d5dSScott Long 		mfi_add_ld(sc, list->ld_list[i].ld.v.target_id);
14828ec5c98bSJohn Baldwin 	skip_add:;
14838ec5c98bSJohn Baldwin 	}
1484c0b332d1SPaul Saab out:
1485c0b332d1SPaul Saab 	if (list)
1486c0b332d1SPaul Saab 		free(list, M_MFIBUF);
1487c0b332d1SPaul Saab 	if (cm)
1488c0b332d1SPaul Saab 		mfi_release_command(cm);
1489441f6d5dSScott Long 
14902e21a3efSScott Long 	return;
14912e21a3efSScott Long }
14922e21a3efSScott Long 
14932137b017SJohn Baldwin /*
14942137b017SJohn Baldwin  * The timestamp is the number of seconds since 00:00 Jan 1, 2000.  If
14952137b017SJohn Baldwin  * the bits in 24-31 are all set, then it is the number of seconds since
14962137b017SJohn Baldwin  * boot.
14972137b017SJohn Baldwin  */
14982137b017SJohn Baldwin static const char *
format_timestamp(uint32_t timestamp)14992137b017SJohn Baldwin format_timestamp(uint32_t timestamp)
15002137b017SJohn Baldwin {
15012137b017SJohn Baldwin 	static char buffer[32];
15022137b017SJohn Baldwin 
15032137b017SJohn Baldwin 	if ((timestamp & 0xff000000) == 0xff000000)
15042137b017SJohn Baldwin 		snprintf(buffer, sizeof(buffer), "boot + %us", timestamp &
15052137b017SJohn Baldwin 		    0x00ffffff);
15062137b017SJohn Baldwin 	else
15072137b017SJohn Baldwin 		snprintf(buffer, sizeof(buffer), "%us", timestamp);
15082137b017SJohn Baldwin 	return (buffer);
15092137b017SJohn Baldwin }
15102137b017SJohn Baldwin 
15112137b017SJohn Baldwin static const char *
format_class(int8_t class)15122137b017SJohn Baldwin format_class(int8_t class)
15132137b017SJohn Baldwin {
15142137b017SJohn Baldwin 	static char buffer[6];
15152137b017SJohn Baldwin 
15162137b017SJohn Baldwin 	switch (class) {
15172137b017SJohn Baldwin 	case MFI_EVT_CLASS_DEBUG:
15182137b017SJohn Baldwin 		return ("debug");
15192137b017SJohn Baldwin 	case MFI_EVT_CLASS_PROGRESS:
15202137b017SJohn Baldwin 		return ("progress");
15212137b017SJohn Baldwin 	case MFI_EVT_CLASS_INFO:
15222137b017SJohn Baldwin 		return ("info");
15232137b017SJohn Baldwin 	case MFI_EVT_CLASS_WARNING:
15242137b017SJohn Baldwin 		return ("WARN");
15252137b017SJohn Baldwin 	case MFI_EVT_CLASS_CRITICAL:
15262137b017SJohn Baldwin 		return ("CRIT");
15272137b017SJohn Baldwin 	case MFI_EVT_CLASS_FATAL:
15282137b017SJohn Baldwin 		return ("FATAL");
15292137b017SJohn Baldwin 	case MFI_EVT_CLASS_DEAD:
15302137b017SJohn Baldwin 		return ("DEAD");
15312137b017SJohn Baldwin 	default:
15322137b017SJohn Baldwin 		snprintf(buffer, sizeof(buffer), "%d", class);
15332137b017SJohn Baldwin 		return (buffer);
15342137b017SJohn Baldwin 	}
15352137b017SJohn Baldwin }
15362137b017SJohn Baldwin 
1537741367d5SDoug Ambrisko static void
mfi_decode_evt(struct mfi_softc * sc,struct mfi_evt_detail * detail)1538741367d5SDoug Ambrisko mfi_decode_evt(struct mfi_softc *sc, struct mfi_evt_detail *detail)
1539741367d5SDoug Ambrisko {
15400d9a4ef3SDoug Ambrisko 	struct mfi_system_pd *syspd = NULL;
15412137b017SJohn Baldwin 
1542406930fbSJung-uk Kim 	device_printf(sc->mfi_dev, "%d (%s/0x%04x/%s) - %s\n", detail->seq,
1543aacea6e2SEd Maste 	    format_timestamp(detail->time), detail->evt_class.members.locale,
1544a6ba0fd6SDoug Ambrisko 	    format_class(detail->evt_class.members.evt_class),
1545a6ba0fd6SDoug Ambrisko 	    detail->description);
1546a6ba0fd6SDoug Ambrisko 
1547a6ba0fd6SDoug Ambrisko         /* Don't act on old AEN's or while shutting down */
1548a6ba0fd6SDoug Ambrisko         if (detail->seq < sc->mfi_boot_seq_num || sc->mfi_detaching)
1549a6ba0fd6SDoug Ambrisko                 return;
1550a6ba0fd6SDoug Ambrisko 
15510d9a4ef3SDoug Ambrisko 	switch (detail->arg_type) {
15520d9a4ef3SDoug Ambrisko 	case MR_EVT_ARGS_NONE:
15530d9a4ef3SDoug Ambrisko 		if (detail->code == MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED) {
15540d9a4ef3SDoug Ambrisko 		    device_printf(sc->mfi_dev, "HostBus scan raised\n");
1555a6ba0fd6SDoug Ambrisko 			if (mfi_detect_jbod_change) {
1556a6ba0fd6SDoug Ambrisko 				/*
1557a6ba0fd6SDoug Ambrisko 				 * Probe for new SYSPD's and Delete
1558a6ba0fd6SDoug Ambrisko 				 * invalid SYSPD's
1559a6ba0fd6SDoug Ambrisko 				 */
15600d9a4ef3SDoug Ambrisko 				sx_xlock(&sc->mfi_config_lock);
15610d9a4ef3SDoug Ambrisko 				mtx_lock(&sc->mfi_io_lock);
15620d9a4ef3SDoug Ambrisko 				mfi_syspdprobe(sc);
15630d9a4ef3SDoug Ambrisko 				mtx_unlock(&sc->mfi_io_lock);
15640d9a4ef3SDoug Ambrisko 				sx_xunlock(&sc->mfi_config_lock);
15650d9a4ef3SDoug Ambrisko 			}
15660d9a4ef3SDoug Ambrisko 		}
15670d9a4ef3SDoug Ambrisko 		break;
15680d9a4ef3SDoug Ambrisko 	case MR_EVT_ARGS_LD_STATE:
1569a6ba0fd6SDoug Ambrisko 		/* During load time driver reads all the events starting
1570a6ba0fd6SDoug Ambrisko 		 * from the one that has been logged after shutdown. Avoid
1571a6ba0fd6SDoug Ambrisko 		 * these old events.
15720d9a4ef3SDoug Ambrisko 		 */
15730d9a4ef3SDoug Ambrisko 		if (detail->args.ld_state.new_state == MFI_LD_STATE_OFFLINE ) {
15740d9a4ef3SDoug Ambrisko 			/* Remove the LD */
1575a6ba0fd6SDoug Ambrisko 			struct mfi_disk *ld;
15760d9a4ef3SDoug Ambrisko 			TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link) {
1577a6ba0fd6SDoug Ambrisko 				if (ld->ld_id ==
1578a6ba0fd6SDoug Ambrisko 				    detail->args.ld_state.ld.target_id)
15790d9a4ef3SDoug Ambrisko 					break;
15800d9a4ef3SDoug Ambrisko 			}
15810d9a4ef3SDoug Ambrisko 			/*
15820d9a4ef3SDoug Ambrisko 			Fix: for kernel panics when SSCD is removed
15830d9a4ef3SDoug Ambrisko 			KASSERT(ld != NULL, ("volume dissappeared"));
15840d9a4ef3SDoug Ambrisko 			*/
1585a6ba0fd6SDoug Ambrisko 			if (ld != NULL) {
1586c6df6f53SWarner Losh 				bus_topo_lock();
15870d9a4ef3SDoug Ambrisko 				device_delete_child(sc->mfi_dev, ld->ld_dev);
1588c6df6f53SWarner Losh 				bus_topo_unlock();
15890d9a4ef3SDoug Ambrisko 			}
15900d9a4ef3SDoug Ambrisko 		}
15910d9a4ef3SDoug Ambrisko 		break;
15920d9a4ef3SDoug Ambrisko 	case MR_EVT_ARGS_PD:
15930d9a4ef3SDoug Ambrisko 		if (detail->code == MR_EVT_PD_REMOVED) {
1594a6ba0fd6SDoug Ambrisko 			if (mfi_detect_jbod_change) {
1595a6ba0fd6SDoug Ambrisko 				/*
1596a6ba0fd6SDoug Ambrisko 				 * If the removed device is a SYSPD then
1597a6ba0fd6SDoug Ambrisko 				 * delete it
1598a6ba0fd6SDoug Ambrisko 				 */
1599a6ba0fd6SDoug Ambrisko 				TAILQ_FOREACH(syspd, &sc->mfi_syspd_tqh,
1600a6ba0fd6SDoug Ambrisko 				    pd_link) {
1601a6ba0fd6SDoug Ambrisko 					if (syspd->pd_id ==
1602a6ba0fd6SDoug Ambrisko 					    detail->args.pd.device_id) {
1603c6df6f53SWarner Losh 						bus_topo_lock();
1604a6ba0fd6SDoug Ambrisko 						device_delete_child(
1605a6ba0fd6SDoug Ambrisko 						    sc->mfi_dev,
1606a6ba0fd6SDoug Ambrisko 						    syspd->pd_dev);
1607c6df6f53SWarner Losh 						bus_topo_unlock();
16080d9a4ef3SDoug Ambrisko 						break;
16090d9a4ef3SDoug Ambrisko 					}
16100d9a4ef3SDoug Ambrisko 				}
16110d9a4ef3SDoug Ambrisko 			}
16120d9a4ef3SDoug Ambrisko 		}
16130d9a4ef3SDoug Ambrisko 		if (detail->code == MR_EVT_PD_INSERTED) {
1614a6ba0fd6SDoug Ambrisko 			if (mfi_detect_jbod_change) {
16150d9a4ef3SDoug Ambrisko 				/* Probe for new SYSPD's */
16160d9a4ef3SDoug Ambrisko 				sx_xlock(&sc->mfi_config_lock);
16170d9a4ef3SDoug Ambrisko 				mtx_lock(&sc->mfi_io_lock);
16180d9a4ef3SDoug Ambrisko 				mfi_syspdprobe(sc);
16190d9a4ef3SDoug Ambrisko 				mtx_unlock(&sc->mfi_io_lock);
16200d9a4ef3SDoug Ambrisko 				sx_xunlock(&sc->mfi_config_lock);
16210d9a4ef3SDoug Ambrisko 			}
16220d9a4ef3SDoug Ambrisko 		}
162359ddd3e9SDoug Ambrisko 		if (sc->mfi_cam_rescan_cb != NULL &&
162459ddd3e9SDoug Ambrisko 		    (detail->code == MR_EVT_PD_INSERTED ||
162559ddd3e9SDoug Ambrisko 		    detail->code == MR_EVT_PD_REMOVED)) {
162659ddd3e9SDoug Ambrisko 			sc->mfi_cam_rescan_cb(sc, detail->args.pd.device_id);
162759ddd3e9SDoug Ambrisko 		}
16280d9a4ef3SDoug Ambrisko 		break;
16290d9a4ef3SDoug Ambrisko 	}
1630a6ba0fd6SDoug Ambrisko }
16312137b017SJohn Baldwin 
1632a6ba0fd6SDoug Ambrisko static void
mfi_queue_evt(struct mfi_softc * sc,struct mfi_evt_detail * detail)1633a6ba0fd6SDoug Ambrisko mfi_queue_evt(struct mfi_softc *sc, struct mfi_evt_detail *detail)
1634a6ba0fd6SDoug Ambrisko {
1635a6ba0fd6SDoug Ambrisko 	struct mfi_evt_queue_elm *elm;
1636a6ba0fd6SDoug Ambrisko 
1637a6ba0fd6SDoug Ambrisko 	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
1638a6ba0fd6SDoug Ambrisko 	elm = malloc(sizeof(*elm), M_MFIBUF, M_NOWAIT|M_ZERO);
1639a6ba0fd6SDoug Ambrisko 	if (elm == NULL)
1640a6ba0fd6SDoug Ambrisko 		return;
1641a6ba0fd6SDoug Ambrisko 	memcpy(&elm->detail, detail, sizeof(*detail));
1642a6ba0fd6SDoug Ambrisko 	TAILQ_INSERT_TAIL(&sc->mfi_evt_queue, elm, link);
1643a6ba0fd6SDoug Ambrisko 	taskqueue_enqueue(taskqueue_swi, &sc->mfi_evt_task);
1644a6ba0fd6SDoug Ambrisko }
1645a6ba0fd6SDoug Ambrisko 
1646a6ba0fd6SDoug Ambrisko static void
mfi_handle_evt(void * context,int pending)1647a6ba0fd6SDoug Ambrisko mfi_handle_evt(void *context, int pending)
1648a6ba0fd6SDoug Ambrisko {
1649a6ba0fd6SDoug Ambrisko 	TAILQ_HEAD(,mfi_evt_queue_elm) queue;
1650a6ba0fd6SDoug Ambrisko 	struct mfi_softc *sc;
1651a6ba0fd6SDoug Ambrisko 	struct mfi_evt_queue_elm *elm;
1652a6ba0fd6SDoug Ambrisko 
1653a6ba0fd6SDoug Ambrisko 	sc = context;
1654a6ba0fd6SDoug Ambrisko 	TAILQ_INIT(&queue);
1655a6ba0fd6SDoug Ambrisko 	mtx_lock(&sc->mfi_io_lock);
1656a6ba0fd6SDoug Ambrisko 	TAILQ_CONCAT(&queue, &sc->mfi_evt_queue, link);
1657a6ba0fd6SDoug Ambrisko 	mtx_unlock(&sc->mfi_io_lock);
1658a6ba0fd6SDoug Ambrisko 	while ((elm = TAILQ_FIRST(&queue)) != NULL) {
1659a6ba0fd6SDoug Ambrisko 		TAILQ_REMOVE(&queue, elm, link);
1660a6ba0fd6SDoug Ambrisko 		mfi_decode_evt(sc, &elm->detail);
1661a6ba0fd6SDoug Ambrisko 		free(elm, M_MFIBUF);
1662a6ba0fd6SDoug Ambrisko 	}
1663741367d5SDoug Ambrisko }
1664741367d5SDoug Ambrisko 
1665741367d5SDoug Ambrisko static int
mfi_aen_register(struct mfi_softc * sc,int seq,int locale)1666741367d5SDoug Ambrisko mfi_aen_register(struct mfi_softc *sc, int seq, int locale)
1667741367d5SDoug Ambrisko {
1668741367d5SDoug Ambrisko 	struct mfi_command *cm;
1669741367d5SDoug Ambrisko 	struct mfi_dcmd_frame *dcmd;
1670741367d5SDoug Ambrisko 	union mfi_evt current_aen, prior_aen;
1671fb595e7aSPaul Saab 	struct mfi_evt_detail *ed = NULL;
1672441f6d5dSScott Long 	int error = 0;
1673741367d5SDoug Ambrisko 
167408c89430SSteven Hartland 	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
167508c89430SSteven Hartland 
1676741367d5SDoug Ambrisko 	current_aen.word = locale;
1677741367d5SDoug Ambrisko 	if (sc->mfi_aen_cm != NULL) {
1678741367d5SDoug Ambrisko 		prior_aen.word =
1679741367d5SDoug Ambrisko 		    ((uint32_t *)&sc->mfi_aen_cm->cm_frame->dcmd.mbox)[1];
1680aacea6e2SEd Maste 		if (prior_aen.members.evt_class <= current_aen.members.evt_class &&
1681741367d5SDoug Ambrisko 		    !((prior_aen.members.locale & current_aen.members.locale)
1682741367d5SDoug Ambrisko 		    ^current_aen.members.locale)) {
1683741367d5SDoug Ambrisko 			return (0);
1684741367d5SDoug Ambrisko 		} else {
1685741367d5SDoug Ambrisko 			prior_aen.members.locale |= current_aen.members.locale;
1686aacea6e2SEd Maste 			if (prior_aen.members.evt_class
1687aacea6e2SEd Maste 			    < current_aen.members.evt_class)
1688aacea6e2SEd Maste 				current_aen.members.evt_class =
1689aacea6e2SEd Maste 				    prior_aen.members.evt_class;
169058ef3154SDoug Ambrisko 			mfi_abort(sc, &sc->mfi_aen_cm);
1691741367d5SDoug Ambrisko 		}
1692741367d5SDoug Ambrisko 	}
1693741367d5SDoug Ambrisko 
1694fb595e7aSPaul Saab 	error = mfi_dcmd_command(sc, &cm, MFI_DCMD_CTRL_EVENT_WAIT,
1695fb595e7aSPaul Saab 	    (void **)&ed, sizeof(*ed));
169608c89430SSteven Hartland 	if (error)
1697441f6d5dSScott Long 		goto out;
1698741367d5SDoug Ambrisko 
1699741367d5SDoug Ambrisko 	dcmd = &cm->cm_frame->dcmd;
1700741367d5SDoug Ambrisko 	((uint32_t *)&dcmd->mbox)[0] = seq;
1701741367d5SDoug Ambrisko 	((uint32_t *)&dcmd->mbox)[1] = locale;
1702741367d5SDoug Ambrisko 	cm->cm_flags = MFI_CMD_DATAIN;
1703741367d5SDoug Ambrisko 	cm->cm_complete = mfi_aen_complete;
1704741367d5SDoug Ambrisko 
17050d9a4ef3SDoug Ambrisko 	sc->last_seq_num = seq;
1706741367d5SDoug Ambrisko 	sc->mfi_aen_cm = cm;
1707741367d5SDoug Ambrisko 
1708741367d5SDoug Ambrisko 	mfi_enqueue_ready(cm);
1709741367d5SDoug Ambrisko 	mfi_startio(sc);
1710741367d5SDoug Ambrisko 
1711441f6d5dSScott Long out:
1712441f6d5dSScott Long 	return (error);
1713741367d5SDoug Ambrisko }
1714741367d5SDoug Ambrisko 
1715741367d5SDoug Ambrisko static void
mfi_aen_complete(struct mfi_command * cm)1716741367d5SDoug Ambrisko mfi_aen_complete(struct mfi_command *cm)
1717741367d5SDoug Ambrisko {
1718741367d5SDoug Ambrisko 	struct mfi_frame_header *hdr;
1719741367d5SDoug Ambrisko 	struct mfi_softc *sc;
1720741367d5SDoug Ambrisko 	struct mfi_evt_detail *detail;
1721441f6d5dSScott Long 	struct mfi_aen *mfi_aen_entry, *tmp;
1722741367d5SDoug Ambrisko 	int seq = 0, aborted = 0;
1723741367d5SDoug Ambrisko 
1724741367d5SDoug Ambrisko 	sc = cm->cm_sc;
17250d9a4ef3SDoug Ambrisko 	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
17260d9a4ef3SDoug Ambrisko 
1727741367d5SDoug Ambrisko 	if (sc->mfi_aen_cm == NULL)
1728741367d5SDoug Ambrisko 		return;
1729741367d5SDoug Ambrisko 
173008c89430SSteven Hartland 	hdr = &cm->cm_frame->header;
173108c89430SSteven Hartland 
1732ddbffe7fSDoug Ambrisko 	if (sc->cm_aen_abort ||
1733c2e3261aSJohn Baldwin 	    hdr->cmd_status == MFI_STAT_INVALID_STATUS) {
1734ddbffe7fSDoug Ambrisko 		sc->cm_aen_abort = 0;
1735741367d5SDoug Ambrisko 		aborted = 1;
1736741367d5SDoug Ambrisko 	} else {
1737741367d5SDoug Ambrisko 		sc->mfi_aen_triggered = 1;
1738441f6d5dSScott Long 		if (sc->mfi_poll_waiting) {
1739441f6d5dSScott Long 			sc->mfi_poll_waiting = 0;
1740741367d5SDoug Ambrisko 			selwakeup(&sc->mfi_select);
1741441f6d5dSScott Long 		}
1742741367d5SDoug Ambrisko 		detail = cm->cm_data;
1743a6ba0fd6SDoug Ambrisko 		mfi_queue_evt(sc, detail);
1744741367d5SDoug Ambrisko 		seq = detail->seq + 1;
1745a6ba0fd6SDoug Ambrisko 		TAILQ_FOREACH_SAFE(mfi_aen_entry, &sc->mfi_aen_pids, aen_link,
1746a6ba0fd6SDoug Ambrisko 		    tmp) {
1747741367d5SDoug Ambrisko 			TAILQ_REMOVE(&sc->mfi_aen_pids, mfi_aen_entry,
1748741367d5SDoug Ambrisko 			    aen_link);
1749441f6d5dSScott Long 			PROC_LOCK(mfi_aen_entry->p);
17508451d0ddSKip Macy 			kern_psignal(mfi_aen_entry->p, SIGIO);
1751441f6d5dSScott Long 			PROC_UNLOCK(mfi_aen_entry->p);
1752741367d5SDoug Ambrisko 			free(mfi_aen_entry, M_MFIBUF);
1753741367d5SDoug Ambrisko 		}
1754741367d5SDoug Ambrisko 	}
1755741367d5SDoug Ambrisko 
1756741367d5SDoug Ambrisko 	free(cm->cm_data, M_MFIBUF);
1757741367d5SDoug Ambrisko 	wakeup(&sc->mfi_aen_cm);
175808c89430SSteven Hartland 	sc->mfi_aen_cm = NULL;
1759741367d5SDoug Ambrisko 	mfi_release_command(cm);
1760741367d5SDoug Ambrisko 
1761741367d5SDoug Ambrisko 	/* set it up again so the driver can catch more events */
176208c89430SSteven Hartland 	if (!aborted)
1763741367d5SDoug Ambrisko 		mfi_aen_setup(sc, seq);
1764741367d5SDoug Ambrisko }
1765741367d5SDoug Ambrisko 
1766c1ed06a8SJohn Baldwin #define MAX_EVENTS 15
1767c1ed06a8SJohn Baldwin 
1768741367d5SDoug Ambrisko static int
mfi_parse_entries(struct mfi_softc * sc,int start_seq,int stop_seq)1769c1ed06a8SJohn Baldwin mfi_parse_entries(struct mfi_softc *sc, int start_seq, int stop_seq)
1770741367d5SDoug Ambrisko {
1771741367d5SDoug Ambrisko 	struct mfi_command *cm;
1772741367d5SDoug Ambrisko 	struct mfi_dcmd_frame *dcmd;
177347b470b9SDoug Ambrisko 	struct mfi_evt_list *el;
1774c1ed06a8SJohn Baldwin 	union mfi_evt class_locale;
1775c1ed06a8SJohn Baldwin 	int error, i, seq, size;
1776741367d5SDoug Ambrisko 
177708c89430SSteven Hartland 	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
177808c89430SSteven Hartland 
1779c1ed06a8SJohn Baldwin 	class_locale.members.reserved = 0;
1780c1ed06a8SJohn Baldwin 	class_locale.members.locale = mfi_event_locale;
1781aacea6e2SEd Maste 	class_locale.members.evt_class  = mfi_event_class;
1782741367d5SDoug Ambrisko 
178347b470b9SDoug Ambrisko 	size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail)
178447b470b9SDoug Ambrisko 		* (MAX_EVENTS - 1);
178547b470b9SDoug Ambrisko 	el = malloc(size, M_MFIBUF, M_NOWAIT | M_ZERO);
1786c1ed06a8SJohn Baldwin 	if (el == NULL)
1787741367d5SDoug Ambrisko 		return (ENOMEM);
1788c1ed06a8SJohn Baldwin 
1789c1ed06a8SJohn Baldwin 	for (seq = start_seq;;) {
1790c1ed06a8SJohn Baldwin 		if ((cm = mfi_dequeue_free(sc)) == NULL) {
1791c1ed06a8SJohn Baldwin 			free(el, M_MFIBUF);
1792c1ed06a8SJohn Baldwin 			return (EBUSY);
1793741367d5SDoug Ambrisko 		}
1794741367d5SDoug Ambrisko 
1795741367d5SDoug Ambrisko 		dcmd = &cm->cm_frame->dcmd;
1796741367d5SDoug Ambrisko 		bzero(dcmd->mbox, MFI_MBOX_SIZE);
1797741367d5SDoug Ambrisko 		dcmd->header.cmd = MFI_CMD_DCMD;
1798741367d5SDoug Ambrisko 		dcmd->header.timeout = 0;
179947b470b9SDoug Ambrisko 		dcmd->header.data_len = size;
1800741367d5SDoug Ambrisko 		dcmd->opcode = MFI_DCMD_CTRL_EVENT_GET;
1801741367d5SDoug Ambrisko 		((uint32_t *)&dcmd->mbox)[0] = seq;
1802c1ed06a8SJohn Baldwin 		((uint32_t *)&dcmd->mbox)[1] = class_locale.word;
1803741367d5SDoug Ambrisko 		cm->cm_sg = &dcmd->sgl;
1804741367d5SDoug Ambrisko 		cm->cm_total_frame_size = MFI_DCMD_FRAME_SIZE;
1805741367d5SDoug Ambrisko 		cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_POLLED;
180647b470b9SDoug Ambrisko 		cm->cm_data = el;
180747b470b9SDoug Ambrisko 		cm->cm_len = size;
1808741367d5SDoug Ambrisko 
1809741367d5SDoug Ambrisko 		if ((error = mfi_mapcmd(sc, cm)) != 0) {
1810c1ed06a8SJohn Baldwin 			device_printf(sc->mfi_dev,
1811c1ed06a8SJohn Baldwin 			    "Failed to get controller entries\n");
1812741367d5SDoug Ambrisko 			mfi_release_command(cm);
1813c1ed06a8SJohn Baldwin 			break;
1814741367d5SDoug Ambrisko 		}
1815741367d5SDoug Ambrisko 
1816741367d5SDoug Ambrisko 		bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap,
1817741367d5SDoug Ambrisko 		    BUS_DMASYNC_POSTREAD);
1818741367d5SDoug Ambrisko 		bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap);
1819741367d5SDoug Ambrisko 
1820c1ed06a8SJohn Baldwin 		if (dcmd->header.cmd_status == MFI_STAT_NOT_FOUND) {
1821c1ed06a8SJohn Baldwin 			mfi_release_command(cm);
1822c1ed06a8SJohn Baldwin 			break;
1823c1ed06a8SJohn Baldwin 		}
1824c1ed06a8SJohn Baldwin 		if (dcmd->header.cmd_status != MFI_STAT_OK) {
1825c1ed06a8SJohn Baldwin 			device_printf(sc->mfi_dev,
1826c1ed06a8SJohn Baldwin 			    "Error %d fetching controller entries\n",
1827c1ed06a8SJohn Baldwin 			    dcmd->header.cmd_status);
1828c1ed06a8SJohn Baldwin 			mfi_release_command(cm);
182908c89430SSteven Hartland 			error = EIO;
1830c1ed06a8SJohn Baldwin 			break;
1831c1ed06a8SJohn Baldwin 		}
1832c1ed06a8SJohn Baldwin 		mfi_release_command(cm);
1833c1ed06a8SJohn Baldwin 
183447b470b9SDoug Ambrisko 		for (i = 0; i < el->count; i++) {
1835c1ed06a8SJohn Baldwin 			/*
1836c1ed06a8SJohn Baldwin 			 * If this event is newer than 'stop_seq' then
1837c1ed06a8SJohn Baldwin 			 * break out of the loop.  Note that the log
1838c1ed06a8SJohn Baldwin 			 * is a circular buffer so we have to handle
1839c1ed06a8SJohn Baldwin 			 * the case that our stop point is earlier in
1840c1ed06a8SJohn Baldwin 			 * the buffer than our start point.
1841c1ed06a8SJohn Baldwin 			 */
1842c1ed06a8SJohn Baldwin 			if (el->event[i].seq >= stop_seq) {
1843c1ed06a8SJohn Baldwin 				if (start_seq <= stop_seq)
1844c1ed06a8SJohn Baldwin 					break;
1845c1ed06a8SJohn Baldwin 				else if (el->event[i].seq < start_seq)
1846c1ed06a8SJohn Baldwin 					break;
1847c1ed06a8SJohn Baldwin 			}
1848a6ba0fd6SDoug Ambrisko 			mfi_queue_evt(sc, &el->event[i]);
1849a1adc445SDoug Ambrisko 		}
1850c1ed06a8SJohn Baldwin 		seq = el->event[el->count - 1].seq + 1;
185147b470b9SDoug Ambrisko 	}
1852741367d5SDoug Ambrisko 
1853c1ed06a8SJohn Baldwin 	free(el, M_MFIBUF);
185408c89430SSteven Hartland 	return (error);
1855741367d5SDoug Ambrisko }
1856741367d5SDoug Ambrisko 
18572e21a3efSScott Long static int
mfi_add_ld(struct mfi_softc * sc,int id)1858c0b332d1SPaul Saab mfi_add_ld(struct mfi_softc *sc, int id)
18592e21a3efSScott Long {
18602e21a3efSScott Long 	struct mfi_command *cm;
1861c0b332d1SPaul Saab 	struct mfi_dcmd_frame *dcmd = NULL;
1862c0b332d1SPaul Saab 	struct mfi_ld_info *ld_info = NULL;
186358ef3154SDoug Ambrisko 	struct mfi_disk_pending *ld_pend;
1864c0b332d1SPaul Saab 	int error;
18652e21a3efSScott Long 
1866c0b332d1SPaul Saab 	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
1867c0b332d1SPaul Saab 
186858ef3154SDoug Ambrisko 	ld_pend = malloc(sizeof(*ld_pend), M_MFIBUF, M_NOWAIT | M_ZERO);
186958ef3154SDoug Ambrisko 	if (ld_pend != NULL) {
187058ef3154SDoug Ambrisko 		ld_pend->ld_id = id;
187158ef3154SDoug Ambrisko 		TAILQ_INSERT_TAIL(&sc->mfi_ld_pend_tqh, ld_pend, ld_link);
187258ef3154SDoug Ambrisko 	}
187358ef3154SDoug Ambrisko 
1874c0b332d1SPaul Saab 	error = mfi_dcmd_command(sc, &cm, MFI_DCMD_LD_GET_INFO,
1875c0b332d1SPaul Saab 	    (void **)&ld_info, sizeof(*ld_info));
1876c0b332d1SPaul Saab 	if (error) {
1877c0b332d1SPaul Saab 		device_printf(sc->mfi_dev,
1878c0b332d1SPaul Saab 		    "Failed to allocate for MFI_DCMD_LD_GET_INFO %d\n", error);
1879c0b332d1SPaul Saab 		if (ld_info)
1880c0b332d1SPaul Saab 			free(ld_info, M_MFIBUF);
1881c0b332d1SPaul Saab 		return (error);
1882d21c5e5eSScott Long 	}
1883c0b332d1SPaul Saab 	cm->cm_flags = MFI_CMD_DATAIN;
1884c0b332d1SPaul Saab 	dcmd = &cm->cm_frame->dcmd;
1885c0b332d1SPaul Saab 	dcmd->mbox[0] = id;
188678997d85SDoug Ambrisko 	if (mfi_wait_command(sc, cm) != 0) {
188778997d85SDoug Ambrisko 		device_printf(sc->mfi_dev,
188878997d85SDoug Ambrisko 		    "Failed to get logical drive: %d\n", id);
188978997d85SDoug Ambrisko 		free(ld_info, M_MFIBUF);
189078997d85SDoug Ambrisko 		return (0);
189178997d85SDoug Ambrisko 	}
18920d9a4ef3SDoug Ambrisko 	if (ld_info->ld_config.params.isSSCD != 1)
189378997d85SDoug Ambrisko 		mfi_add_ld_complete(cm);
1894a6ba0fd6SDoug Ambrisko 	else {
18950d9a4ef3SDoug Ambrisko 		mfi_release_command(cm);
18960d9a4ef3SDoug Ambrisko 		if (ld_info)		/* SSCD drives ld_info free here */
18970d9a4ef3SDoug Ambrisko 			free(ld_info, M_MFIBUF);
18980d9a4ef3SDoug Ambrisko 	}
18992e21a3efSScott Long 	return (0);
19002e21a3efSScott Long }
19012e21a3efSScott Long 
19022e21a3efSScott Long static void
mfi_add_ld_complete(struct mfi_command * cm)1903c0b332d1SPaul Saab mfi_add_ld_complete(struct mfi_command *cm)
19042e21a3efSScott Long {
19052e21a3efSScott Long 	struct mfi_frame_header *hdr;
1906c0b332d1SPaul Saab 	struct mfi_ld_info *ld_info;
19072e21a3efSScott Long 	struct mfi_softc *sc;
19082e21a3efSScott Long 	device_t child;
19092e21a3efSScott Long 
1910c0b332d1SPaul Saab 	sc = cm->cm_sc;
1911c0b332d1SPaul Saab 	hdr = &cm->cm_frame->header;
1912c0b332d1SPaul Saab 	ld_info = cm->cm_private;
1913c0b332d1SPaul Saab 
191458ef3154SDoug Ambrisko 	if (sc->cm_map_abort || hdr->cmd_status != MFI_STAT_OK) {
1915c0b332d1SPaul Saab 		free(ld_info, M_MFIBUF);
191658ef3154SDoug Ambrisko 		wakeup(&sc->mfi_map_sync_cm);
1917c0b332d1SPaul Saab 		mfi_release_command(cm);
1918c0b332d1SPaul Saab 		return;
191920df0de4SScott Long 	}
192058ef3154SDoug Ambrisko 	wakeup(&sc->mfi_map_sync_cm);
1921c0b332d1SPaul Saab 	mfi_release_command(cm);
192220df0de4SScott Long 
192335ef86f2SScott Long 	mtx_unlock(&sc->mfi_io_lock);
1924c6df6f53SWarner Losh 	bus_topo_lock();
1925*b670c9baSAhmad Khalifa 	if ((child = device_add_child(sc->mfi_dev, "mfid",
1926*b670c9baSAhmad Khalifa 	    DEVICE_UNIT_ANY)) == NULL) {
19272e21a3efSScott Long 		device_printf(sc->mfi_dev, "Failed to add logical disk\n");
1928c0b332d1SPaul Saab 		free(ld_info, M_MFIBUF);
1929c6df6f53SWarner Losh 		bus_topo_unlock();
193035ef86f2SScott Long 		mtx_lock(&sc->mfi_io_lock);
1931c0b332d1SPaul Saab 		return;
19322e21a3efSScott Long 	}
19332e21a3efSScott Long 
1934ddfae47bSScott Long 	device_set_ivars(child, ld_info);
19352e21a3efSScott Long 	device_set_desc(child, "MFI Logical Disk");
193618250ec6SJohn Baldwin 	bus_attach_children(sc->mfi_dev);
1937c6df6f53SWarner Losh 	bus_topo_unlock();
19382e21a3efSScott Long 	mtx_lock(&sc->mfi_io_lock);
19392e21a3efSScott Long }
19406d678f8eSScott Long 
mfi_add_sys_pd(struct mfi_softc * sc,int id)19410d9a4ef3SDoug Ambrisko static int mfi_add_sys_pd(struct mfi_softc *sc, int id)
19420d9a4ef3SDoug Ambrisko {
19430d9a4ef3SDoug Ambrisko 	struct mfi_command *cm;
19440d9a4ef3SDoug Ambrisko 	struct mfi_dcmd_frame *dcmd = NULL;
19450d9a4ef3SDoug Ambrisko 	struct mfi_pd_info *pd_info = NULL;
194658ef3154SDoug Ambrisko 	struct mfi_system_pending *syspd_pend;
19470d9a4ef3SDoug Ambrisko 	int error;
19480d9a4ef3SDoug Ambrisko 
19490d9a4ef3SDoug Ambrisko 	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
19500d9a4ef3SDoug Ambrisko 
195158ef3154SDoug Ambrisko 	syspd_pend = malloc(sizeof(*syspd_pend), M_MFIBUF, M_NOWAIT | M_ZERO);
195258ef3154SDoug Ambrisko 	if (syspd_pend != NULL) {
195358ef3154SDoug Ambrisko 		syspd_pend->pd_id = id;
195458ef3154SDoug Ambrisko 		TAILQ_INSERT_TAIL(&sc->mfi_syspd_pend_tqh, syspd_pend, pd_link);
195558ef3154SDoug Ambrisko 	}
195658ef3154SDoug Ambrisko 
19570d9a4ef3SDoug Ambrisko 	error = mfi_dcmd_command(sc, &cm, MFI_DCMD_PD_GET_INFO,
19580d9a4ef3SDoug Ambrisko 		(void **)&pd_info, sizeof(*pd_info));
19590d9a4ef3SDoug Ambrisko 	if (error) {
19600d9a4ef3SDoug Ambrisko 		device_printf(sc->mfi_dev,
1961a6ba0fd6SDoug Ambrisko 		    "Failed to allocated for MFI_DCMD_PD_GET_INFO %d\n",
1962a6ba0fd6SDoug Ambrisko 		    error);
19630d9a4ef3SDoug Ambrisko 		if (pd_info)
19640d9a4ef3SDoug Ambrisko 			free(pd_info, M_MFIBUF);
19650d9a4ef3SDoug Ambrisko 		return (error);
19660d9a4ef3SDoug Ambrisko 	}
19670d9a4ef3SDoug Ambrisko 	cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_POLLED;
19680d9a4ef3SDoug Ambrisko 	dcmd = &cm->cm_frame->dcmd;
19690d9a4ef3SDoug Ambrisko 	dcmd->mbox[0]=id;
19700d9a4ef3SDoug Ambrisko 	dcmd->header.scsi_status = 0;
19710d9a4ef3SDoug Ambrisko 	dcmd->header.pad0 = 0;
197208c89430SSteven Hartland 	if ((error = mfi_mapcmd(sc, cm)) != 0) {
19730d9a4ef3SDoug Ambrisko 		device_printf(sc->mfi_dev,
19740d9a4ef3SDoug Ambrisko 		    "Failed to get physical drive info %d\n", id);
19750d9a4ef3SDoug Ambrisko 		free(pd_info, M_MFIBUF);
197608c89430SSteven Hartland 		mfi_release_command(cm);
197708c89430SSteven Hartland 		return (error);
19780d9a4ef3SDoug Ambrisko 	}
1979a6ba0fd6SDoug Ambrisko 	bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap,
1980a6ba0fd6SDoug Ambrisko 	    BUS_DMASYNC_POSTREAD);
19810d9a4ef3SDoug Ambrisko 	bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap);
19820d9a4ef3SDoug Ambrisko 	mfi_add_sys_pd_complete(cm);
19830d9a4ef3SDoug Ambrisko 	return (0);
19840d9a4ef3SDoug Ambrisko }
19850d9a4ef3SDoug Ambrisko 
19860d9a4ef3SDoug Ambrisko static void
mfi_add_sys_pd_complete(struct mfi_command * cm)19870d9a4ef3SDoug Ambrisko mfi_add_sys_pd_complete(struct mfi_command *cm)
19880d9a4ef3SDoug Ambrisko {
19890d9a4ef3SDoug Ambrisko 	struct mfi_frame_header *hdr;
19900d9a4ef3SDoug Ambrisko 	struct mfi_pd_info *pd_info;
19910d9a4ef3SDoug Ambrisko 	struct mfi_softc *sc;
19920d9a4ef3SDoug Ambrisko 	device_t child;
19930d9a4ef3SDoug Ambrisko 
19940d9a4ef3SDoug Ambrisko 	sc = cm->cm_sc;
19950d9a4ef3SDoug Ambrisko 	hdr = &cm->cm_frame->header;
19960d9a4ef3SDoug Ambrisko 	pd_info = cm->cm_private;
19970d9a4ef3SDoug Ambrisko 
19980d9a4ef3SDoug Ambrisko 	if (hdr->cmd_status != MFI_STAT_OK) {
19990d9a4ef3SDoug Ambrisko 		free(pd_info, M_MFIBUF);
20000d9a4ef3SDoug Ambrisko 		mfi_release_command(cm);
20010d9a4ef3SDoug Ambrisko 		return;
20020d9a4ef3SDoug Ambrisko 	}
20030d9a4ef3SDoug Ambrisko 	if (pd_info->fw_state != MFI_PD_STATE_SYSTEM) {
20040d9a4ef3SDoug Ambrisko 		device_printf(sc->mfi_dev, "PD=%x is not SYSTEM PD\n",
20050d9a4ef3SDoug Ambrisko 		    pd_info->ref.v.device_id);
20060d9a4ef3SDoug Ambrisko 		free(pd_info, M_MFIBUF);
20070d9a4ef3SDoug Ambrisko 		mfi_release_command(cm);
20080d9a4ef3SDoug Ambrisko 		return;
20090d9a4ef3SDoug Ambrisko 	}
20100d9a4ef3SDoug Ambrisko 	mfi_release_command(cm);
20110d9a4ef3SDoug Ambrisko 
20120d9a4ef3SDoug Ambrisko 	mtx_unlock(&sc->mfi_io_lock);
2013c6df6f53SWarner Losh 	bus_topo_lock();
2014*b670c9baSAhmad Khalifa 	if ((child = device_add_child(sc->mfi_dev, "mfisyspd",
2015*b670c9baSAhmad Khalifa 	    DEVICE_UNIT_ANY)) == NULL) {
20160d9a4ef3SDoug Ambrisko 		device_printf(sc->mfi_dev, "Failed to add system pd\n");
20170d9a4ef3SDoug Ambrisko 		free(pd_info, M_MFIBUF);
2018c6df6f53SWarner Losh 		bus_topo_unlock();
20190d9a4ef3SDoug Ambrisko 		mtx_lock(&sc->mfi_io_lock);
20200d9a4ef3SDoug Ambrisko 		return;
20210d9a4ef3SDoug Ambrisko 	}
20220d9a4ef3SDoug Ambrisko 
20230d9a4ef3SDoug Ambrisko 	device_set_ivars(child, pd_info);
20240d9a4ef3SDoug Ambrisko 	device_set_desc(child, "MFI System PD");
202518250ec6SJohn Baldwin 	bus_attach_children(sc->mfi_dev);
2026c6df6f53SWarner Losh 	bus_topo_unlock();
20270d9a4ef3SDoug Ambrisko 	mtx_lock(&sc->mfi_io_lock);
20280d9a4ef3SDoug Ambrisko }
202952d5baa2SDoug Ambrisko 
20302e21a3efSScott Long static struct mfi_command *
mfi_bio_command(struct mfi_softc * sc)20312e21a3efSScott Long mfi_bio_command(struct mfi_softc *sc)
20322e21a3efSScott Long {
20332e21a3efSScott Long 	struct bio *bio;
20340d9a4ef3SDoug Ambrisko 	struct mfi_command *cm = NULL;
20350d9a4ef3SDoug Ambrisko 
20360d9a4ef3SDoug Ambrisko 	/*reserving two commands to avoid starvation for IOCTL*/
20370d9a4ef3SDoug Ambrisko 	if (sc->mfi_qstat[MFIQ_FREE].q_length < 2) {
20380d9a4ef3SDoug Ambrisko 		return (NULL);
20390d9a4ef3SDoug Ambrisko 	}
20400d9a4ef3SDoug Ambrisko 	if ((bio = mfi_dequeue_bio(sc)) == NULL) {
20410d9a4ef3SDoug Ambrisko 		return (NULL);
20420d9a4ef3SDoug Ambrisko 	}
20430d9a4ef3SDoug Ambrisko 	if ((uintptr_t)bio->bio_driver2 == MFI_LD_IO) {
20440d9a4ef3SDoug Ambrisko 		cm = mfi_build_ldio(sc, bio);
20450d9a4ef3SDoug Ambrisko 	} else if ((uintptr_t) bio->bio_driver2 == MFI_SYS_PD_IO) {
20460d9a4ef3SDoug Ambrisko 		cm = mfi_build_syspdio(sc, bio);
20470d9a4ef3SDoug Ambrisko 	}
20480d9a4ef3SDoug Ambrisko 	if (!cm)
20490d9a4ef3SDoug Ambrisko 	    mfi_enqueue_bio(sc, bio);
20500d9a4ef3SDoug Ambrisko 	return cm;
20510d9a4ef3SDoug Ambrisko }
2052808df726SXin LI 
205358ef3154SDoug Ambrisko /*
205458ef3154SDoug Ambrisko  * mostly copied from cam/scsi/scsi_all.c:scsi_read_write
205558ef3154SDoug Ambrisko  */
205658ef3154SDoug Ambrisko 
205758ef3154SDoug Ambrisko int
mfi_build_cdb(int readop,uint8_t byte2,u_int64_t lba,u_int32_t block_count,uint8_t * cdb)205858ef3154SDoug Ambrisko mfi_build_cdb(int readop, uint8_t byte2, u_int64_t lba, u_int32_t block_count, uint8_t *cdb)
2059808df726SXin LI {
2060808df726SXin LI 	int cdb_len;
2061808df726SXin LI 
2062808df726SXin LI 	if (((lba & 0x1fffff) == lba)
2063808df726SXin LI          && ((block_count & 0xff) == block_count)
2064808df726SXin LI          && (byte2 == 0)) {
2065808df726SXin LI 		/* We can fit in a 6 byte cdb */
2066808df726SXin LI 		struct scsi_rw_6 *scsi_cmd;
2067808df726SXin LI 
206858ef3154SDoug Ambrisko 		scsi_cmd = (struct scsi_rw_6 *)cdb;
2069808df726SXin LI 		scsi_cmd->opcode = readop ? READ_6 : WRITE_6;
2070808df726SXin LI 		scsi_ulto3b(lba, scsi_cmd->addr);
2071808df726SXin LI 		scsi_cmd->length = block_count & 0xff;
2072808df726SXin LI 		scsi_cmd->control = 0;
2073808df726SXin LI 		cdb_len = sizeof(*scsi_cmd);
2074808df726SXin LI 	} else if (((block_count & 0xffff) == block_count) && ((lba & 0xffffffff) == lba)) {
2075808df726SXin LI 		/* Need a 10 byte CDB */
2076808df726SXin LI 		struct scsi_rw_10 *scsi_cmd;
2077808df726SXin LI 
207858ef3154SDoug Ambrisko 		scsi_cmd = (struct scsi_rw_10 *)cdb;
2079808df726SXin LI 		scsi_cmd->opcode = readop ? READ_10 : WRITE_10;
2080808df726SXin LI 		scsi_cmd->byte2 = byte2;
2081808df726SXin LI 		scsi_ulto4b(lba, scsi_cmd->addr);
2082808df726SXin LI 		scsi_cmd->reserved = 0;
2083808df726SXin LI 		scsi_ulto2b(block_count, scsi_cmd->length);
2084808df726SXin LI 		scsi_cmd->control = 0;
2085808df726SXin LI 		cdb_len = sizeof(*scsi_cmd);
2086808df726SXin LI 	} else if (((block_count & 0xffffffff) == block_count) &&
2087808df726SXin LI 	    ((lba & 0xffffffff) == lba)) {
2088808df726SXin LI 		/* Block count is too big for 10 byte CDB use a 12 byte CDB */
2089808df726SXin LI 		struct scsi_rw_12 *scsi_cmd;
2090808df726SXin LI 
209158ef3154SDoug Ambrisko 		scsi_cmd = (struct scsi_rw_12 *)cdb;
2092808df726SXin LI 		scsi_cmd->opcode = readop ? READ_12 : WRITE_12;
2093808df726SXin LI 		scsi_cmd->byte2 = byte2;
2094808df726SXin LI 		scsi_ulto4b(lba, scsi_cmd->addr);
2095808df726SXin LI 		scsi_cmd->reserved = 0;
2096808df726SXin LI 		scsi_ulto4b(block_count, scsi_cmd->length);
2097808df726SXin LI 		scsi_cmd->control = 0;
2098808df726SXin LI 		cdb_len = sizeof(*scsi_cmd);
2099808df726SXin LI 	} else {
2100808df726SXin LI 		/*
2101808df726SXin LI 		 * 16 byte CDB.  We'll only get here if the LBA is larger
2102808df726SXin LI 		 * than 2^32
2103808df726SXin LI 		 */
2104808df726SXin LI 		struct scsi_rw_16 *scsi_cmd;
2105808df726SXin LI 
210658ef3154SDoug Ambrisko 		scsi_cmd = (struct scsi_rw_16 *)cdb;
2107808df726SXin LI 		scsi_cmd->opcode = readop ? READ_16 : WRITE_16;
2108808df726SXin LI 		scsi_cmd->byte2 = byte2;
2109808df726SXin LI 		scsi_u64to8b(lba, scsi_cmd->addr);
2110808df726SXin LI 		scsi_cmd->reserved = 0;
2111808df726SXin LI 		scsi_ulto4b(block_count, scsi_cmd->length);
2112808df726SXin LI 		scsi_cmd->control = 0;
2113808df726SXin LI 		cdb_len = sizeof(*scsi_cmd);
2114808df726SXin LI 	}
2115808df726SXin LI 
2116808df726SXin LI 	return cdb_len;
2117808df726SXin LI }
2118808df726SXin LI 
2119c33ce503SKonstantin Belousov extern char *unmapped_buf;
2120c33ce503SKonstantin Belousov 
21210d9a4ef3SDoug Ambrisko static struct mfi_command *
mfi_build_syspdio(struct mfi_softc * sc,struct bio * bio)21220d9a4ef3SDoug Ambrisko mfi_build_syspdio(struct mfi_softc *sc, struct bio *bio)
21230d9a4ef3SDoug Ambrisko {
21240d9a4ef3SDoug Ambrisko 	struct mfi_command *cm;
21250d9a4ef3SDoug Ambrisko 	struct mfi_pass_frame *pass;
212658ef3154SDoug Ambrisko 	uint32_t context = 0;
212758ef3154SDoug Ambrisko 	int flags = 0, blkcount = 0, readop;
2128808df726SXin LI 	uint8_t cdb_len;
21292e21a3efSScott Long 
213008c89430SSteven Hartland 	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
213108c89430SSteven Hartland 
21322e21a3efSScott Long 	if ((cm = mfi_dequeue_free(sc)) == NULL)
21332e21a3efSScott Long 	    return (NULL);
21342e21a3efSScott Long 
21350d9a4ef3SDoug Ambrisko 	/* Zero out the MFI frame */
21360d9a4ef3SDoug Ambrisko 	context = cm->cm_frame->header.context;
21370d9a4ef3SDoug Ambrisko 	bzero(cm->cm_frame, sizeof(union mfi_frame));
21380d9a4ef3SDoug Ambrisko 	cm->cm_frame->header.context = context;
21390d9a4ef3SDoug Ambrisko 	pass = &cm->cm_frame->pass;
21400d9a4ef3SDoug Ambrisko 	bzero(pass->cdb, 16);
21410d9a4ef3SDoug Ambrisko 	pass->header.cmd = MFI_CMD_PD_SCSI_IO;
2142225e4b42SWarner Losh 	switch (bio->bio_cmd) {
21430d9a4ef3SDoug Ambrisko 	case BIO_READ:
2144c33ce503SKonstantin Belousov 		flags = MFI_CMD_DATAIN | MFI_CMD_BIO;
214558ef3154SDoug Ambrisko 		readop = 1;
21460d9a4ef3SDoug Ambrisko 		break;
21470d9a4ef3SDoug Ambrisko 	case BIO_WRITE:
2148c33ce503SKonstantin Belousov 		flags = MFI_CMD_DATAOUT | MFI_CMD_BIO;
214958ef3154SDoug Ambrisko 		readop = 0;
21500d9a4ef3SDoug Ambrisko 		break;
21510d9a4ef3SDoug Ambrisko 	default:
2152808df726SXin LI 		/* TODO: what about BIO_DELETE??? */
2153d176b803SScott Long 		biofinish(bio, NULL, EOPNOTSUPP);
2154d176b803SScott Long 		mfi_enqueue_free(cm);
2155d176b803SScott Long 		return (NULL);
21562e21a3efSScott Long 	}
21572e21a3efSScott Long 
21580d9a4ef3SDoug Ambrisko 	/* Cheat with the sector length to avoid a non-constant division */
2159057b4402SPedro F. Giffuni 	blkcount = howmany(bio->bio_bcount, MFI_SECTOR_LEN);
21600d9a4ef3SDoug Ambrisko 	/* Fill the LBA and Transfer length in CDB */
216158ef3154SDoug Ambrisko 	cdb_len = mfi_build_cdb(readop, 0, bio->bio_pblkno, blkcount,
216258ef3154SDoug Ambrisko 	    pass->cdb);
21630d9a4ef3SDoug Ambrisko 	pass->header.target_id = (uintptr_t)bio->bio_driver1;
216458ef3154SDoug Ambrisko 	pass->header.lun_id = 0;
21650d9a4ef3SDoug Ambrisko 	pass->header.timeout = 0;
21660d9a4ef3SDoug Ambrisko 	pass->header.flags = 0;
21670d9a4ef3SDoug Ambrisko 	pass->header.scsi_status = 0;
21680d9a4ef3SDoug Ambrisko 	pass->header.sense_len = MFI_SENSE_LEN;
21690d9a4ef3SDoug Ambrisko 	pass->header.data_len = bio->bio_bcount;
2170808df726SXin LI 	pass->header.cdb_len = cdb_len;
2171a6ba0fd6SDoug Ambrisko 	pass->sense_addr_lo = (uint32_t)cm->cm_sense_busaddr;
2172a6ba0fd6SDoug Ambrisko 	pass->sense_addr_hi = (uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32);
21730d9a4ef3SDoug Ambrisko 	cm->cm_complete = mfi_bio_complete;
21740d9a4ef3SDoug Ambrisko 	cm->cm_private = bio;
2175c33ce503SKonstantin Belousov 	cm->cm_data = unmapped_buf;
21760d9a4ef3SDoug Ambrisko 	cm->cm_len = bio->bio_bcount;
21770d9a4ef3SDoug Ambrisko 	cm->cm_sg = &pass->sgl;
21780d9a4ef3SDoug Ambrisko 	cm->cm_total_frame_size = MFI_PASS_FRAME_SIZE;
21790d9a4ef3SDoug Ambrisko 	cm->cm_flags = flags;
218008c89430SSteven Hartland 
21810d9a4ef3SDoug Ambrisko 	return (cm);
21820d9a4ef3SDoug Ambrisko }
21830d9a4ef3SDoug Ambrisko 
21840d9a4ef3SDoug Ambrisko static struct mfi_command *
mfi_build_ldio(struct mfi_softc * sc,struct bio * bio)21850d9a4ef3SDoug Ambrisko mfi_build_ldio(struct mfi_softc *sc, struct bio *bio)
21860d9a4ef3SDoug Ambrisko {
21870d9a4ef3SDoug Ambrisko 	struct mfi_io_frame *io;
21880d9a4ef3SDoug Ambrisko 	struct mfi_command *cm;
2189808df726SXin LI 	int flags;
2190808df726SXin LI 	uint32_t blkcount;
21910d9a4ef3SDoug Ambrisko 	uint32_t context = 0;
21920d9a4ef3SDoug Ambrisko 
219308c89430SSteven Hartland 	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
219408c89430SSteven Hartland 
21950d9a4ef3SDoug Ambrisko 	if ((cm = mfi_dequeue_free(sc)) == NULL)
21960d9a4ef3SDoug Ambrisko 	    return (NULL);
21970d9a4ef3SDoug Ambrisko 
21980d9a4ef3SDoug Ambrisko 	/* Zero out the MFI frame */
21990d9a4ef3SDoug Ambrisko 	context = cm->cm_frame->header.context;
22000d9a4ef3SDoug Ambrisko 	bzero(cm->cm_frame, sizeof(union mfi_frame));
22010d9a4ef3SDoug Ambrisko 	cm->cm_frame->header.context = context;
22022e21a3efSScott Long 	io = &cm->cm_frame->io;
2203225e4b42SWarner Losh 	switch (bio->bio_cmd) {
22042e21a3efSScott Long 	case BIO_READ:
22052e21a3efSScott Long 		io->header.cmd = MFI_CMD_LD_READ;
2206c33ce503SKonstantin Belousov 		flags = MFI_CMD_DATAIN | MFI_CMD_BIO;
22072e21a3efSScott Long 		break;
22082e21a3efSScott Long 	case BIO_WRITE:
22092e21a3efSScott Long 		io->header.cmd = MFI_CMD_LD_WRITE;
2210c33ce503SKonstantin Belousov 		flags = MFI_CMD_DATAOUT | MFI_CMD_BIO;
22112e21a3efSScott Long 		break;
22122e21a3efSScott Long 	default:
2213808df726SXin LI 		/* TODO: what about BIO_DELETE??? */
2214d176b803SScott Long 		biofinish(bio, NULL, EOPNOTSUPP);
2215d176b803SScott Long 		mfi_enqueue_free(cm);
2216d176b803SScott Long 		return (NULL);
22172e21a3efSScott Long 	}
22182e21a3efSScott Long 
22192e21a3efSScott Long 	/* Cheat with the sector length to avoid a non-constant division */
2220057b4402SPedro F. Giffuni 	blkcount = howmany(bio->bio_bcount, MFI_SECTOR_LEN);
22212e21a3efSScott Long 	io->header.target_id = (uintptr_t)bio->bio_driver1;
22222e21a3efSScott Long 	io->header.timeout = 0;
22232e21a3efSScott Long 	io->header.flags = 0;
22240d9a4ef3SDoug Ambrisko 	io->header.scsi_status = 0;
22252e21a3efSScott Long 	io->header.sense_len = MFI_SENSE_LEN;
22262e21a3efSScott Long 	io->header.data_len = blkcount;
2227a6ba0fd6SDoug Ambrisko 	io->sense_addr_lo = (uint32_t)cm->cm_sense_busaddr;
2228a6ba0fd6SDoug Ambrisko 	io->sense_addr_hi = (uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32);
22292e21a3efSScott Long 	io->lba_hi = (bio->bio_pblkno & 0xffffffff00000000) >> 32;
22302e21a3efSScott Long 	io->lba_lo = bio->bio_pblkno & 0xffffffff;
22312e21a3efSScott Long 	cm->cm_complete = mfi_bio_complete;
22322e21a3efSScott Long 	cm->cm_private = bio;
2233c33ce503SKonstantin Belousov 	cm->cm_data = unmapped_buf;
22342e21a3efSScott Long 	cm->cm_len = bio->bio_bcount;
22352e21a3efSScott Long 	cm->cm_sg = &io->sgl;
22362e21a3efSScott Long 	cm->cm_total_frame_size = MFI_IO_FRAME_SIZE;
22372e21a3efSScott Long 	cm->cm_flags = flags;
223808c89430SSteven Hartland 
22392e21a3efSScott Long 	return (cm);
22402e21a3efSScott Long }
22412e21a3efSScott Long 
22422e21a3efSScott Long static void
mfi_bio_complete(struct mfi_command * cm)22432e21a3efSScott Long mfi_bio_complete(struct mfi_command *cm)
22442e21a3efSScott Long {
22452e21a3efSScott Long 	struct bio *bio;
22462e21a3efSScott Long 	struct mfi_frame_header *hdr;
22472e21a3efSScott Long 	struct mfi_softc *sc;
22482e21a3efSScott Long 
22492e21a3efSScott Long 	bio = cm->cm_private;
22502e21a3efSScott Long 	hdr = &cm->cm_frame->header;
22512e21a3efSScott Long 	sc = cm->cm_sc;
22522e21a3efSScott Long 
2253c2e3261aSJohn Baldwin 	if ((hdr->cmd_status != MFI_STAT_OK) || (hdr->scsi_status != 0)) {
22542e21a3efSScott Long 		bio->bio_flags |= BIO_ERROR;
22552e21a3efSScott Long 		bio->bio_error = EIO;
225608c89430SSteven Hartland 		device_printf(sc->mfi_dev, "I/O error, cmd=%p, status=%#x, "
225708c89430SSteven Hartland 		    "scsi_status=%#x\n", cm, hdr->cmd_status, hdr->scsi_status);
22582e21a3efSScott Long 		mfi_print_sense(cm->cm_sc, cm->cm_sense);
2259fa1e6ef4SDoug Ambrisko 	} else if (cm->cm_error != 0) {
2260fa1e6ef4SDoug Ambrisko 		bio->bio_flags |= BIO_ERROR;
226108c89430SSteven Hartland 		bio->bio_error = cm->cm_error;
226208c89430SSteven Hartland 		device_printf(sc->mfi_dev, "I/O error, cmd=%p, error=%#x\n",
226308c89430SSteven Hartland 		    cm, cm->cm_error);
22642e21a3efSScott Long 	}
22652e21a3efSScott Long 
22662e21a3efSScott Long 	mfi_release_command(cm);
22672e21a3efSScott Long 	mfi_disk_complete(bio);
22682e21a3efSScott Long }
22692e21a3efSScott Long 
22702e21a3efSScott Long void
mfi_startio(struct mfi_softc * sc)22712e21a3efSScott Long mfi_startio(struct mfi_softc *sc)
22722e21a3efSScott Long {
22732e21a3efSScott Long 	struct mfi_command *cm;
227435ef86f2SScott Long 	struct ccb_hdr *ccbh;
22752e21a3efSScott Long 
22762e21a3efSScott Long 	for (;;) {
22772e21a3efSScott Long 		/* Don't bother if we're short on resources */
22782e21a3efSScott Long 		if (sc->mfi_flags & MFI_FLAGS_QFRZN)
22792e21a3efSScott Long 			break;
22802e21a3efSScott Long 
22812e21a3efSScott Long 		/* Try a command that has already been prepared */
22822e21a3efSScott Long 		cm = mfi_dequeue_ready(sc);
22832e21a3efSScott Long 
228435ef86f2SScott Long 		if (cm == NULL) {
228535ef86f2SScott Long 			if ((ccbh = TAILQ_FIRST(&sc->mfi_cam_ccbq)) != NULL)
228635ef86f2SScott Long 				cm = sc->mfi_cam_start(ccbh);
228735ef86f2SScott Long 		}
228835ef86f2SScott Long 
22892e21a3efSScott Long 		/* Nope, so look for work on the bioq */
22902e21a3efSScott Long 		if (cm == NULL)
22912e21a3efSScott Long 			cm = mfi_bio_command(sc);
22922e21a3efSScott Long 
22932e21a3efSScott Long 		/* No work available, so exit */
22942e21a3efSScott Long 		if (cm == NULL)
22952e21a3efSScott Long 			break;
22962e21a3efSScott Long 
22972e21a3efSScott Long 		/* Send the command to the controller */
22982e21a3efSScott Long 		if (mfi_mapcmd(sc, cm) != 0) {
229908c89430SSteven Hartland 			device_printf(sc->mfi_dev, "Failed to startio\n");
23002e21a3efSScott Long 			mfi_requeue_ready(cm);
23012e21a3efSScott Long 			break;
23022e21a3efSScott Long 		}
23032e21a3efSScott Long 	}
23042e21a3efSScott Long }
23052e21a3efSScott Long 
23060d9a4ef3SDoug Ambrisko int
mfi_mapcmd(struct mfi_softc * sc,struct mfi_command * cm)23072e21a3efSScott Long mfi_mapcmd(struct mfi_softc *sc, struct mfi_command *cm)
23082e21a3efSScott Long {
23092e21a3efSScott Long 	int error, polled;
23102e21a3efSScott Long 
2311441f6d5dSScott Long 	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
2312441f6d5dSScott Long 
23130d9a4ef3SDoug Ambrisko 	if ((cm->cm_data != NULL) && (cm->cm_frame->header.cmd != MFI_CMD_STP )) {
23142e21a3efSScott Long 		polled = (cm->cm_flags & MFI_CMD_POLLED) ? BUS_DMA_NOWAIT : 0;
2315dd0b4fb6SKonstantin Belousov 		if (cm->cm_flags & MFI_CMD_CCB)
2316dd0b4fb6SKonstantin Belousov 			error = bus_dmamap_load_ccb(sc->mfi_buffer_dmat,
2317dd0b4fb6SKonstantin Belousov 			    cm->cm_dmamap, cm->cm_data, mfi_data_cb, cm,
2318dd0b4fb6SKonstantin Belousov 			    polled);
2319c33ce503SKonstantin Belousov 		else if (cm->cm_flags & MFI_CMD_BIO)
2320c33ce503SKonstantin Belousov 			error = bus_dmamap_load_bio(sc->mfi_buffer_dmat,
2321c33ce503SKonstantin Belousov 			    cm->cm_dmamap, cm->cm_private, mfi_data_cb, cm,
2322c33ce503SKonstantin Belousov 			    polled);
2323dd0b4fb6SKonstantin Belousov 		else
2324dd0b4fb6SKonstantin Belousov 			error = bus_dmamap_load(sc->mfi_buffer_dmat,
2325dd0b4fb6SKonstantin Belousov 			    cm->cm_dmamap, cm->cm_data, cm->cm_len,
2326dd0b4fb6SKonstantin Belousov 			    mfi_data_cb, cm, polled);
23272e21a3efSScott Long 		if (error == EINPROGRESS) {
23282e21a3efSScott Long 			sc->mfi_flags |= MFI_FLAGS_QFRZN;
23292e21a3efSScott Long 			return (0);
23302e21a3efSScott Long 		}
23312e21a3efSScott Long 	} else {
23322e21a3efSScott Long 		error = mfi_send_frame(sc, cm);
23332e21a3efSScott Long 	}
23342e21a3efSScott Long 
23352e21a3efSScott Long 	return (error);
23362e21a3efSScott Long }
23372e21a3efSScott Long 
23382e21a3efSScott Long static void
mfi_data_cb(void * arg,bus_dma_segment_t * segs,int nsegs,int error)23392e21a3efSScott Long mfi_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
23402e21a3efSScott Long {
23412e21a3efSScott Long 	struct mfi_frame_header *hdr;
23422e21a3efSScott Long 	struct mfi_command *cm;
2343aab58269SScott Long 	union mfi_sgl *sgl;
23442e21a3efSScott Long 	struct mfi_softc *sc;
23455f8ad41cSAlexander Motin 	int i, j, first, dir;
234608c89430SSteven Hartland 	int sge_size, locked;
23472e21a3efSScott Long 
23482e21a3efSScott Long 	cm = (struct mfi_command *)arg;
23492e21a3efSScott Long 	sc = cm->cm_sc;
2350aab58269SScott Long 	hdr = &cm->cm_frame->header;
2351aab58269SScott Long 	sgl = cm->cm_sg;
23522e21a3efSScott Long 
235308c89430SSteven Hartland 	/*
235408c89430SSteven Hartland 	 * We need to check if we have the lock as this is async
235508c89430SSteven Hartland 	 * callback so even though our caller mfi_mapcmd asserts
2356453130d9SPedro F. Giffuni 	 * it has the lock, there is no guarantee that hasn't been
235708c89430SSteven Hartland 	 * dropped if bus_dmamap_load returned prior to our
235808c89430SSteven Hartland 	 * completion.
235908c89430SSteven Hartland 	 */
236008c89430SSteven Hartland 	if ((locked = mtx_owned(&sc->mfi_io_lock)) == 0)
236108c89430SSteven Hartland 		mtx_lock(&sc->mfi_io_lock);
236208c89430SSteven Hartland 
23635be25877SDoug Ambrisko 	if (error) {
23645be25877SDoug Ambrisko 		printf("error %d in callback\n", error);
23655be25877SDoug Ambrisko 		cm->cm_error = error;
23665be25877SDoug Ambrisko 		mfi_complete(sc, cm);
236708c89430SSteven Hartland 		goto out;
23685be25877SDoug Ambrisko 	}
23690d9a4ef3SDoug Ambrisko 	/* Use IEEE sgl only for IO's on a SKINNY controller
23700d9a4ef3SDoug Ambrisko 	 * For other commands on a SKINNY controller use either
23710d9a4ef3SDoug Ambrisko 	 * sg32 or sg64 based on the sizeof(bus_addr_t).
23720d9a4ef3SDoug Ambrisko 	 * Also calculate the total frame size based on the type
23730d9a4ef3SDoug Ambrisko 	 * of SGL used.
23740d9a4ef3SDoug Ambrisko 	 */
23750d9a4ef3SDoug Ambrisko 	if (((cm->cm_frame->header.cmd == MFI_CMD_PD_SCSI_IO) ||
23760d9a4ef3SDoug Ambrisko 	    (cm->cm_frame->header.cmd == MFI_CMD_LD_READ) ||
23770d9a4ef3SDoug Ambrisko 	    (cm->cm_frame->header.cmd == MFI_CMD_LD_WRITE)) &&
23780d9a4ef3SDoug Ambrisko 	    (sc->mfi_flags & MFI_FLAGS_SKINNY)) {
23790d9a4ef3SDoug Ambrisko 		for (i = 0; i < nsegs; i++) {
23800d9a4ef3SDoug Ambrisko 			sgl->sg_skinny[i].addr = segs[i].ds_addr;
23810d9a4ef3SDoug Ambrisko 			sgl->sg_skinny[i].len = segs[i].ds_len;
23820d9a4ef3SDoug Ambrisko 			sgl->sg_skinny[i].flag = 0;
23830d9a4ef3SDoug Ambrisko 		}
23840d9a4ef3SDoug Ambrisko 		hdr->flags |= MFI_FRAME_IEEE_SGL | MFI_FRAME_SGL64;
23850d9a4ef3SDoug Ambrisko 		sge_size = sizeof(struct mfi_sg_skinny);
2386a6ba0fd6SDoug Ambrisko 		hdr->sg_count = nsegs;
23870d9a4ef3SDoug Ambrisko 	} else {
23885f8ad41cSAlexander Motin 		j = 0;
23895f8ad41cSAlexander Motin 		if (cm->cm_frame->header.cmd == MFI_CMD_STP) {
23905f8ad41cSAlexander Motin 			first = cm->cm_stp_len;
23915f8ad41cSAlexander Motin 			if ((sc->mfi_flags & MFI_FLAGS_SG64) == 0) {
23925f8ad41cSAlexander Motin 				sgl->sg32[j].addr = segs[0].ds_addr;
23935f8ad41cSAlexander Motin 				sgl->sg32[j++].len = first;
23945f8ad41cSAlexander Motin 			} else {
23955f8ad41cSAlexander Motin 				sgl->sg64[j].addr = segs[0].ds_addr;
23965f8ad41cSAlexander Motin 				sgl->sg64[j++].len = first;
23975f8ad41cSAlexander Motin 			}
23985f8ad41cSAlexander Motin 		} else
23995f8ad41cSAlexander Motin 			first = 0;
2400aab58269SScott Long 		if ((sc->mfi_flags & MFI_FLAGS_SG64) == 0) {
24012e21a3efSScott Long 			for (i = 0; i < nsegs; i++) {
24025f8ad41cSAlexander Motin 				sgl->sg32[j].addr = segs[i].ds_addr + first;
24035f8ad41cSAlexander Motin 				sgl->sg32[j++].len = segs[i].ds_len - first;
24045f8ad41cSAlexander Motin 				first = 0;
24052e21a3efSScott Long 			}
2406aab58269SScott Long 		} else {
2407aab58269SScott Long 			for (i = 0; i < nsegs; i++) {
24085f8ad41cSAlexander Motin 				sgl->sg64[j].addr = segs[i].ds_addr + first;
24095f8ad41cSAlexander Motin 				sgl->sg64[j++].len = segs[i].ds_len - first;
24105f8ad41cSAlexander Motin 				first = 0;
2411aab58269SScott Long 			}
2412aab58269SScott Long 			hdr->flags |= MFI_FRAME_SGL64;
24132e21a3efSScott Long 		}
24145f8ad41cSAlexander Motin 		hdr->sg_count = j;
2415a6ba0fd6SDoug Ambrisko 		sge_size = sc->mfi_sge_size;
24160d9a4ef3SDoug Ambrisko 	}
24172e21a3efSScott Long 
24182e21a3efSScott Long 	dir = 0;
24192e21a3efSScott Long 	if (cm->cm_flags & MFI_CMD_DATAIN) {
24202e21a3efSScott Long 		dir |= BUS_DMASYNC_PREREAD;
24212e21a3efSScott Long 		hdr->flags |= MFI_FRAME_DIR_READ;
24222e21a3efSScott Long 	}
24232e21a3efSScott Long 	if (cm->cm_flags & MFI_CMD_DATAOUT) {
24242e21a3efSScott Long 		dir |= BUS_DMASYNC_PREWRITE;
24252e21a3efSScott Long 		hdr->flags |= MFI_FRAME_DIR_WRITE;
24262e21a3efSScott Long 	}
24272e21a3efSScott Long 	bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap, dir);
24282e21a3efSScott Long 	cm->cm_flags |= MFI_CMD_MAPPED;
24292e21a3efSScott Long 
24302e21a3efSScott Long 	/*
24312e21a3efSScott Long 	 * Instead of calculating the total number of frames in the
24322e21a3efSScott Long 	 * compound frame, it's already assumed that there will be at
24332e21a3efSScott Long 	 * least 1 frame, so don't compensate for the modulo of the
24342e21a3efSScott Long 	 * following division.
24352e21a3efSScott Long 	 */
24366ef1ad0dSScott Long 	cm->cm_total_frame_size += (sge_size * nsegs);
24372e21a3efSScott Long 	cm->cm_extra_frames = (cm->cm_total_frame_size - 1) / MFI_FRAME_SIZE;
24382e21a3efSScott Long 
243908c89430SSteven Hartland 	if ((error = mfi_send_frame(sc, cm)) != 0) {
244008c89430SSteven Hartland 		printf("error %d in callback from mfi_send_frame\n", error);
244108c89430SSteven Hartland 		cm->cm_error = error;
244208c89430SSteven Hartland 		mfi_complete(sc, cm);
244308c89430SSteven Hartland 		goto out;
244408c89430SSteven Hartland 	}
244508c89430SSteven Hartland 
244608c89430SSteven Hartland out:
244708c89430SSteven Hartland 	/* leave the lock in the state we found it */
244808c89430SSteven Hartland 	if (locked == 0)
244908c89430SSteven Hartland 		mtx_unlock(&sc->mfi_io_lock);
24502e21a3efSScott Long 
24512e21a3efSScott Long 	return;
24522e21a3efSScott Long }
24532e21a3efSScott Long 
24542e21a3efSScott Long static int
mfi_send_frame(struct mfi_softc * sc,struct mfi_command * cm)24552e21a3efSScott Long mfi_send_frame(struct mfi_softc *sc, struct mfi_command *cm)
24562e21a3efSScott Long {
245708c89430SSteven Hartland 	int error;
245808c89430SSteven Hartland 
245908c89430SSteven Hartland 	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
246008c89430SSteven Hartland 
246108c89430SSteven Hartland 	if (sc->MFA_enabled)
246208c89430SSteven Hartland 		error = mfi_tbolt_send_frame(sc, cm);
246308c89430SSteven Hartland 	else
246408c89430SSteven Hartland 		error = mfi_std_send_frame(sc, cm);
246508c89430SSteven Hartland 
246608c89430SSteven Hartland 	if (error != 0 && (cm->cm_flags & MFI_ON_MFIQ_BUSY) != 0)
246708c89430SSteven Hartland 		mfi_remove_busy(cm);
246808c89430SSteven Hartland 
246908c89430SSteven Hartland 	return (error);
247008c89430SSteven Hartland }
247108c89430SSteven Hartland 
247208c89430SSteven Hartland static int
mfi_std_send_frame(struct mfi_softc * sc,struct mfi_command * cm)247308c89430SSteven Hartland mfi_std_send_frame(struct mfi_softc *sc, struct mfi_command *cm)
247408c89430SSteven Hartland {
24753c77cee1SScott Long 	struct mfi_frame_header *hdr;
247608c89430SSteven Hartland 	int tm = mfi_polled_cmd_timeout * 1000;
24773c77cee1SScott Long 
24783c77cee1SScott Long 	hdr = &cm->cm_frame->header;
24793c77cee1SScott Long 
24803c77cee1SScott Long 	if ((cm->cm_flags & MFI_CMD_POLLED) == 0) {
24813c77cee1SScott Long 		cm->cm_timestamp = time_uptime;
24823c77cee1SScott Long 		mfi_enqueue_busy(cm);
24833c77cee1SScott Long 	} else {
2484c2e3261aSJohn Baldwin 		hdr->cmd_status = MFI_STAT_INVALID_STATUS;
24853c77cee1SScott Long 		hdr->flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE;
24863c77cee1SScott Long 	}
24872e21a3efSScott Long 
24882e21a3efSScott Long 	/*
24892e21a3efSScott Long 	 * The bus address of the command is aligned on a 64 byte boundary,
24902e21a3efSScott Long 	 * leaving the least 6 bits as zero.  For whatever reason, the
24912e21a3efSScott Long 	 * hardware wants the address shifted right by three, leaving just
249278e36c27SScott Long 	 * 3 zero bits.  These three bits are then used as a prefetching
249378e36c27SScott Long 	 * hint for the hardware to predict how many frames need to be
249478e36c27SScott Long 	 * fetched across the bus.  If a command has more than 8 frames
249578e36c27SScott Long 	 * then the 3 bits are set to 0x7 and the firmware uses other
249678e36c27SScott Long 	 * information in the command to determine the total amount to fetch.
249778e36c27SScott Long 	 * However, FreeBSD doesn't support I/O larger than 128K, so 8 frames
249878e36c27SScott Long 	 * is enough for both 32bit and 64bit systems.
24992e21a3efSScott Long 	 */
250078e36c27SScott Long 	if (cm->cm_extra_frames > 7)
250178e36c27SScott Long 		cm->cm_extra_frames = 7;
250278e36c27SScott Long 
2503610f2ef3SScott Long 	sc->mfi_issue_cmd(sc, cm->cm_frame_busaddr, cm->cm_extra_frames);
25043c77cee1SScott Long 
25053c77cee1SScott Long 	if ((cm->cm_flags & MFI_CMD_POLLED) == 0)
25063c77cee1SScott Long 		return (0);
25073c77cee1SScott Long 
25083c77cee1SScott Long 	/* This is a polled command, so busy-wait for it to complete. */
2509c2e3261aSJohn Baldwin 	while (hdr->cmd_status == MFI_STAT_INVALID_STATUS) {
25103c77cee1SScott Long 		DELAY(1000);
2511812819c7SDoug Ambrisko 		tm -= 1;
25123c77cee1SScott Long 		if (tm <= 0)
25133c77cee1SScott Long 			break;
25143c77cee1SScott Long 	}
25153c77cee1SScott Long 
2516c2e3261aSJohn Baldwin 	if (hdr->cmd_status == MFI_STAT_INVALID_STATUS) {
2517812819c7SDoug Ambrisko 		device_printf(sc->mfi_dev, "Frame %p timed out "
2518812819c7SDoug Ambrisko 		    "command 0x%X\n", hdr, cm->cm_frame->dcmd.opcode);
25193c77cee1SScott Long 		return (ETIMEDOUT);
25203c77cee1SScott Long 	}
25213c77cee1SScott Long 
25222e21a3efSScott Long 	return (0);
25232e21a3efSScott Long }
25242e21a3efSScott Long 
25250d9a4ef3SDoug Ambrisko void
mfi_complete(struct mfi_softc * sc,struct mfi_command * cm)25262e21a3efSScott Long mfi_complete(struct mfi_softc *sc, struct mfi_command *cm)
25272e21a3efSScott Long {
25282e21a3efSScott Long 	int dir;
252908c89430SSteven Hartland 	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
25302e21a3efSScott Long 
25312e21a3efSScott Long 	if ((cm->cm_flags & MFI_CMD_MAPPED) != 0) {
25322e21a3efSScott Long 		dir = 0;
25335f8ad41cSAlexander Motin 		if ((cm->cm_flags & MFI_CMD_DATAIN) ||
25345f8ad41cSAlexander Motin 		    (cm->cm_frame->header.cmd == MFI_CMD_STP))
25352e21a3efSScott Long 			dir |= BUS_DMASYNC_POSTREAD;
25362e21a3efSScott Long 		if (cm->cm_flags & MFI_CMD_DATAOUT)
25372e21a3efSScott Long 			dir |= BUS_DMASYNC_POSTWRITE;
25382e21a3efSScott Long 
25392e21a3efSScott Long 		bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap, dir);
25402e21a3efSScott Long 		bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap);
25412e21a3efSScott Long 		cm->cm_flags &= ~MFI_CMD_MAPPED;
25422e21a3efSScott Long 	}
25432e21a3efSScott Long 
25445be25877SDoug Ambrisko 	cm->cm_flags |= MFI_CMD_COMPLETED;
25455be25877SDoug Ambrisko 
25462e21a3efSScott Long 	if (cm->cm_complete != NULL)
25472e21a3efSScott Long 		cm->cm_complete(cm);
2548c0b332d1SPaul Saab 	else
2549c0b332d1SPaul Saab 		wakeup(cm);
25502e21a3efSScott Long }
25512e21a3efSScott Long 
2552741367d5SDoug Ambrisko static int
mfi_abort(struct mfi_softc * sc,struct mfi_command ** cm_abort)255358ef3154SDoug Ambrisko mfi_abort(struct mfi_softc *sc, struct mfi_command **cm_abort)
2554741367d5SDoug Ambrisko {
2555741367d5SDoug Ambrisko 	struct mfi_command *cm;
2556741367d5SDoug Ambrisko 	struct mfi_abort_frame *abort;
255708c89430SSteven Hartland 	int i = 0, error;
25580d9a4ef3SDoug Ambrisko 	uint32_t context = 0;
2559741367d5SDoug Ambrisko 
256058ef3154SDoug Ambrisko 	mtx_lock(&sc->mfi_io_lock);
2561741367d5SDoug Ambrisko 	if ((cm = mfi_dequeue_free(sc)) == NULL) {
256208c89430SSteven Hartland 		mtx_unlock(&sc->mfi_io_lock);
2563741367d5SDoug Ambrisko 		return (EBUSY);
2564741367d5SDoug Ambrisko 	}
2565741367d5SDoug Ambrisko 
25660d9a4ef3SDoug Ambrisko 	/* Zero out the MFI frame */
25670d9a4ef3SDoug Ambrisko 	context = cm->cm_frame->header.context;
25680d9a4ef3SDoug Ambrisko 	bzero(cm->cm_frame, sizeof(union mfi_frame));
25690d9a4ef3SDoug Ambrisko 	cm->cm_frame->header.context = context;
25700d9a4ef3SDoug Ambrisko 
2571741367d5SDoug Ambrisko 	abort = &cm->cm_frame->abort;
2572741367d5SDoug Ambrisko 	abort->header.cmd = MFI_CMD_ABORT;
2573741367d5SDoug Ambrisko 	abort->header.flags = 0;
25740d9a4ef3SDoug Ambrisko 	abort->header.scsi_status = 0;
257558ef3154SDoug Ambrisko 	abort->abort_context = (*cm_abort)->cm_frame->header.context;
257658ef3154SDoug Ambrisko 	abort->abort_mfi_addr_lo = (uint32_t)(*cm_abort)->cm_frame_busaddr;
2577a6ba0fd6SDoug Ambrisko 	abort->abort_mfi_addr_hi =
257858ef3154SDoug Ambrisko 		(uint32_t)((uint64_t)(*cm_abort)->cm_frame_busaddr >> 32);
2579741367d5SDoug Ambrisko 	cm->cm_data = NULL;
25803c77cee1SScott Long 	cm->cm_flags = MFI_CMD_POLLED;
2581741367d5SDoug Ambrisko 
258208c89430SSteven Hartland 	if ((error = mfi_mapcmd(sc, cm)) != 0)
258308c89430SSteven Hartland 		device_printf(sc->mfi_dev, "failed to abort command\n");
2584741367d5SDoug Ambrisko 	mfi_release_command(cm);
2585741367d5SDoug Ambrisko 
258658ef3154SDoug Ambrisko 	mtx_unlock(&sc->mfi_io_lock);
258758ef3154SDoug Ambrisko 	while (i < 5 && *cm_abort != NULL) {
258858ef3154SDoug Ambrisko 		tsleep(cm_abort, 0, "mfiabort",
2589a6ba0fd6SDoug Ambrisko 		    5 * hz);
2590812819c7SDoug Ambrisko 		i++;
2591741367d5SDoug Ambrisko 	}
259258ef3154SDoug Ambrisko 	if (*cm_abort != NULL) {
259358ef3154SDoug Ambrisko 		/* Force a complete if command didn't abort */
259458ef3154SDoug Ambrisko 		mtx_lock(&sc->mfi_io_lock);
259558ef3154SDoug Ambrisko 		(*cm_abort)->cm_complete(*cm_abort);
259658ef3154SDoug Ambrisko 		mtx_unlock(&sc->mfi_io_lock);
2597ddbffe7fSDoug Ambrisko 	}
2598741367d5SDoug Ambrisko 
259908c89430SSteven Hartland 	return (error);
2600741367d5SDoug Ambrisko }
2601741367d5SDoug Ambrisko 
26022e21a3efSScott Long int
mfi_dump_blocks(struct mfi_softc * sc,int id,uint64_t lba,void * virt,int len)2603a6ba0fd6SDoug Ambrisko mfi_dump_blocks(struct mfi_softc *sc, int id, uint64_t lba, void *virt,
2604a6ba0fd6SDoug Ambrisko      int len)
26052e21a3efSScott Long {
26062e21a3efSScott Long 	struct mfi_command *cm;
26072e21a3efSScott Long 	struct mfi_io_frame *io;
26082e21a3efSScott Long 	int error;
26090d9a4ef3SDoug Ambrisko 	uint32_t context = 0;
26102e21a3efSScott Long 
26112e21a3efSScott Long 	if ((cm = mfi_dequeue_free(sc)) == NULL)
26122e21a3efSScott Long 		return (EBUSY);
26132e21a3efSScott Long 
26140d9a4ef3SDoug Ambrisko 	/* Zero out the MFI frame */
26150d9a4ef3SDoug Ambrisko 	context = cm->cm_frame->header.context;
26160d9a4ef3SDoug Ambrisko 	bzero(cm->cm_frame, sizeof(union mfi_frame));
26170d9a4ef3SDoug Ambrisko 	cm->cm_frame->header.context = context;
26180d9a4ef3SDoug Ambrisko 
26192e21a3efSScott Long 	io = &cm->cm_frame->io;
26202e21a3efSScott Long 	io->header.cmd = MFI_CMD_LD_WRITE;
26212e21a3efSScott Long 	io->header.target_id = id;
26222e21a3efSScott Long 	io->header.timeout = 0;
26232e21a3efSScott Long 	io->header.flags = 0;
26240d9a4ef3SDoug Ambrisko 	io->header.scsi_status = 0;
26252e21a3efSScott Long 	io->header.sense_len = MFI_SENSE_LEN;
2626057b4402SPedro F. Giffuni 	io->header.data_len = howmany(len, MFI_SECTOR_LEN);
2627a6ba0fd6SDoug Ambrisko 	io->sense_addr_lo = (uint32_t)cm->cm_sense_busaddr;
2628a6ba0fd6SDoug Ambrisko 	io->sense_addr_hi = (uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32);
26292e21a3efSScott Long 	io->lba_hi = (lba & 0xffffffff00000000) >> 32;
26302e21a3efSScott Long 	io->lba_lo = lba & 0xffffffff;
26312e21a3efSScott Long 	cm->cm_data = virt;
26322e21a3efSScott Long 	cm->cm_len = len;
26332e21a3efSScott Long 	cm->cm_sg = &io->sgl;
26342e21a3efSScott Long 	cm->cm_total_frame_size = MFI_IO_FRAME_SIZE;
26352e21a3efSScott Long 	cm->cm_flags = MFI_CMD_POLLED | MFI_CMD_DATAOUT;
26362e21a3efSScott Long 
263708c89430SSteven Hartland 	if ((error = mfi_mapcmd(sc, cm)) != 0)
263808c89430SSteven Hartland 		device_printf(sc->mfi_dev, "failed dump blocks\n");
26392e21a3efSScott Long 	bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap,
26402e21a3efSScott Long 	    BUS_DMASYNC_POSTWRITE);
26412e21a3efSScott Long 	bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap);
26422e21a3efSScott Long 	mfi_release_command(cm);
26432e21a3efSScott Long 
26442e21a3efSScott Long 	return (error);
26452e21a3efSScott Long }
26462e21a3efSScott Long 
26470d9a4ef3SDoug Ambrisko int
mfi_dump_syspd_blocks(struct mfi_softc * sc,int id,uint64_t lba,void * virt,int len)2648a6ba0fd6SDoug Ambrisko mfi_dump_syspd_blocks(struct mfi_softc *sc, int id, uint64_t lba, void *virt,
2649a6ba0fd6SDoug Ambrisko     int len)
26500d9a4ef3SDoug Ambrisko {
26510d9a4ef3SDoug Ambrisko 	struct mfi_command *cm;
26520d9a4ef3SDoug Ambrisko 	struct mfi_pass_frame *pass;
265358ef3154SDoug Ambrisko 	int error, readop, cdb_len;
2654808df726SXin LI 	uint32_t blkcount;
26550d9a4ef3SDoug Ambrisko 
26560d9a4ef3SDoug Ambrisko 	if ((cm = mfi_dequeue_free(sc)) == NULL)
26570d9a4ef3SDoug Ambrisko 		return (EBUSY);
26580d9a4ef3SDoug Ambrisko 
26590d9a4ef3SDoug Ambrisko 	pass = &cm->cm_frame->pass;
26600d9a4ef3SDoug Ambrisko 	bzero(pass->cdb, 16);
26610d9a4ef3SDoug Ambrisko 	pass->header.cmd = MFI_CMD_PD_SCSI_IO;
266258ef3154SDoug Ambrisko 
266358ef3154SDoug Ambrisko 	readop = 0;
2664057b4402SPedro F. Giffuni 	blkcount = howmany(len, MFI_SECTOR_LEN);
266558ef3154SDoug Ambrisko 	cdb_len = mfi_build_cdb(readop, 0, lba, blkcount, pass->cdb);
26660d9a4ef3SDoug Ambrisko 	pass->header.target_id = id;
26670d9a4ef3SDoug Ambrisko 	pass->header.timeout = 0;
26680d9a4ef3SDoug Ambrisko 	pass->header.flags = 0;
26690d9a4ef3SDoug Ambrisko 	pass->header.scsi_status = 0;
26700d9a4ef3SDoug Ambrisko 	pass->header.sense_len = MFI_SENSE_LEN;
26710d9a4ef3SDoug Ambrisko 	pass->header.data_len = len;
267258ef3154SDoug Ambrisko 	pass->header.cdb_len = cdb_len;
2673a6ba0fd6SDoug Ambrisko 	pass->sense_addr_lo = (uint32_t)cm->cm_sense_busaddr;
2674a6ba0fd6SDoug Ambrisko 	pass->sense_addr_hi = (uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32);
26750d9a4ef3SDoug Ambrisko 	cm->cm_data = virt;
26760d9a4ef3SDoug Ambrisko 	cm->cm_len = len;
26770d9a4ef3SDoug Ambrisko 	cm->cm_sg = &pass->sgl;
26780d9a4ef3SDoug Ambrisko 	cm->cm_total_frame_size = MFI_PASS_FRAME_SIZE;
267958ef3154SDoug Ambrisko 	cm->cm_flags = MFI_CMD_POLLED | MFI_CMD_DATAOUT | MFI_CMD_SCSI;
26800d9a4ef3SDoug Ambrisko 
268108c89430SSteven Hartland 	if ((error = mfi_mapcmd(sc, cm)) != 0)
268208c89430SSteven Hartland 		device_printf(sc->mfi_dev, "failed dump blocks\n");
26830d9a4ef3SDoug Ambrisko 	bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap,
26840d9a4ef3SDoug Ambrisko 	    BUS_DMASYNC_POSTWRITE);
26850d9a4ef3SDoug Ambrisko 	bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap);
26860d9a4ef3SDoug Ambrisko 	mfi_release_command(cm);
26870d9a4ef3SDoug Ambrisko 
26880d9a4ef3SDoug Ambrisko 	return (error);
26890d9a4ef3SDoug Ambrisko }
26900d9a4ef3SDoug Ambrisko 
26912e21a3efSScott Long static int
mfi_open(struct cdev * dev,int flags,int fmt,struct thread * td)269200b4e54aSWarner Losh mfi_open(struct cdev *dev, int flags, int fmt, struct thread *td)
26932e21a3efSScott Long {
26942e21a3efSScott Long 	struct mfi_softc *sc;
2695cde586a7SJohn Baldwin 	int error;
26962e21a3efSScott Long 
26972e21a3efSScott Long 	sc = dev->si_drv1;
2698441f6d5dSScott Long 
2699441f6d5dSScott Long 	mtx_lock(&sc->mfi_io_lock);
2700cde586a7SJohn Baldwin 	if (sc->mfi_detaching)
2701cde586a7SJohn Baldwin 		error = ENXIO;
2702cde586a7SJohn Baldwin 	else {
27032e21a3efSScott Long 		sc->mfi_flags |= MFI_FLAGS_OPEN;
2704cde586a7SJohn Baldwin 		error = 0;
2705cde586a7SJohn Baldwin 	}
2706441f6d5dSScott Long 	mtx_unlock(&sc->mfi_io_lock);
27072e21a3efSScott Long 
2708cde586a7SJohn Baldwin 	return (error);
27092e21a3efSScott Long }
27102e21a3efSScott Long 
27112e21a3efSScott Long static int
mfi_close(struct cdev * dev,int flags,int fmt,struct thread * td)271200b4e54aSWarner Losh mfi_close(struct cdev *dev, int flags, int fmt, struct thread *td)
27132e21a3efSScott Long {
27142e21a3efSScott Long 	struct mfi_softc *sc;
2715441f6d5dSScott Long 	struct mfi_aen *mfi_aen_entry, *tmp;
27162e21a3efSScott Long 
27172e21a3efSScott Long 	sc = dev->si_drv1;
2718441f6d5dSScott Long 
2719441f6d5dSScott Long 	mtx_lock(&sc->mfi_io_lock);
27202e21a3efSScott Long 	sc->mfi_flags &= ~MFI_FLAGS_OPEN;
27212e21a3efSScott Long 
2722441f6d5dSScott Long 	TAILQ_FOREACH_SAFE(mfi_aen_entry, &sc->mfi_aen_pids, aen_link, tmp) {
2723741367d5SDoug Ambrisko 		if (mfi_aen_entry->p == curproc) {
2724741367d5SDoug Ambrisko 			TAILQ_REMOVE(&sc->mfi_aen_pids, mfi_aen_entry,
2725741367d5SDoug Ambrisko 			    aen_link);
2726741367d5SDoug Ambrisko 			free(mfi_aen_entry, M_MFIBUF);
2727741367d5SDoug Ambrisko 		}
2728741367d5SDoug Ambrisko 	}
2729441f6d5dSScott Long 	mtx_unlock(&sc->mfi_io_lock);
27302e21a3efSScott Long 	return (0);
27312e21a3efSScott Long }
27322e21a3efSScott Long 
27332e21a3efSScott Long static int
mfi_config_lock(struct mfi_softc * sc,uint32_t opcode)27348ec5c98bSJohn Baldwin mfi_config_lock(struct mfi_softc *sc, uint32_t opcode)
27358ec5c98bSJohn Baldwin {
27368ec5c98bSJohn Baldwin 
27378ec5c98bSJohn Baldwin 	switch (opcode) {
27388ec5c98bSJohn Baldwin 	case MFI_DCMD_LD_DELETE:
27398ec5c98bSJohn Baldwin 	case MFI_DCMD_CFG_ADD:
27408ec5c98bSJohn Baldwin 	case MFI_DCMD_CFG_CLEAR:
27417f6194d6SJohn Baldwin 	case MFI_DCMD_CFG_FOREIGN_IMPORT:
27428ec5c98bSJohn Baldwin 		sx_xlock(&sc->mfi_config_lock);
27438ec5c98bSJohn Baldwin 		return (1);
27448ec5c98bSJohn Baldwin 	default:
27458ec5c98bSJohn Baldwin 		return (0);
27468ec5c98bSJohn Baldwin 	}
27478ec5c98bSJohn Baldwin }
27488ec5c98bSJohn Baldwin 
27498ec5c98bSJohn Baldwin static void
mfi_config_unlock(struct mfi_softc * sc,int locked)27508ec5c98bSJohn Baldwin mfi_config_unlock(struct mfi_softc *sc, int locked)
27518ec5c98bSJohn Baldwin {
27528ec5c98bSJohn Baldwin 
27538ec5c98bSJohn Baldwin 	if (locked)
27548ec5c98bSJohn Baldwin 		sx_xunlock(&sc->mfi_config_lock);
27558ec5c98bSJohn Baldwin }
27568ec5c98bSJohn Baldwin 
2757a6ba0fd6SDoug Ambrisko /*
2758a6ba0fd6SDoug Ambrisko  * Perform pre-issue checks on commands from userland and possibly veto
2759a6ba0fd6SDoug Ambrisko  * them.
2760a6ba0fd6SDoug Ambrisko  */
27618ec5c98bSJohn Baldwin static int
mfi_check_command_pre(struct mfi_softc * sc,struct mfi_command * cm)27628ec5c98bSJohn Baldwin mfi_check_command_pre(struct mfi_softc *sc, struct mfi_command *cm)
27638ec5c98bSJohn Baldwin {
27648ec5c98bSJohn Baldwin 	struct mfi_disk *ld, *ld2;
27658ec5c98bSJohn Baldwin 	int error;
27660d9a4ef3SDoug Ambrisko 	struct mfi_system_pd *syspd = NULL;
27670d9a4ef3SDoug Ambrisko 	uint16_t syspd_id;
27680d9a4ef3SDoug Ambrisko 	uint16_t *mbox;
27698ec5c98bSJohn Baldwin 
27708ec5c98bSJohn Baldwin 	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
27718ec5c98bSJohn Baldwin 	error = 0;
27728ec5c98bSJohn Baldwin 	switch (cm->cm_frame->dcmd.opcode) {
27738ec5c98bSJohn Baldwin 	case MFI_DCMD_LD_DELETE:
27748ec5c98bSJohn Baldwin 		TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link) {
27758ec5c98bSJohn Baldwin 			if (ld->ld_id == cm->cm_frame->dcmd.mbox[0])
27768ec5c98bSJohn Baldwin 				break;
27778ec5c98bSJohn Baldwin 		}
27788ec5c98bSJohn Baldwin 		if (ld == NULL)
27798ec5c98bSJohn Baldwin 			error = ENOENT;
27808ec5c98bSJohn Baldwin 		else
27818ec5c98bSJohn Baldwin 			error = mfi_disk_disable(ld);
27828ec5c98bSJohn Baldwin 		break;
27838ec5c98bSJohn Baldwin 	case MFI_DCMD_CFG_CLEAR:
27848ec5c98bSJohn Baldwin 		TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link) {
27858ec5c98bSJohn Baldwin 			error = mfi_disk_disable(ld);
27868ec5c98bSJohn Baldwin 			if (error)
27878ec5c98bSJohn Baldwin 				break;
27888ec5c98bSJohn Baldwin 		}
27898ec5c98bSJohn Baldwin 		if (error) {
27908ec5c98bSJohn Baldwin 			TAILQ_FOREACH(ld2, &sc->mfi_ld_tqh, ld_link) {
27918ec5c98bSJohn Baldwin 				if (ld2 == ld)
27928ec5c98bSJohn Baldwin 					break;
27938ec5c98bSJohn Baldwin 				mfi_disk_enable(ld2);
27948ec5c98bSJohn Baldwin 			}
27958ec5c98bSJohn Baldwin 		}
27968ec5c98bSJohn Baldwin 		break;
27970d9a4ef3SDoug Ambrisko 	case MFI_DCMD_PD_STATE_SET:
27980d9a4ef3SDoug Ambrisko 		mbox = (uint16_t *) cm->cm_frame->dcmd.mbox;
27990d9a4ef3SDoug Ambrisko 		syspd_id = mbox[0];
28000d9a4ef3SDoug Ambrisko 		if (mbox[2] == MFI_PD_STATE_UNCONFIGURED_GOOD) {
28010d9a4ef3SDoug Ambrisko 			TAILQ_FOREACH(syspd, &sc->mfi_syspd_tqh, pd_link) {
28020d9a4ef3SDoug Ambrisko 				if (syspd->pd_id == syspd_id)
28030d9a4ef3SDoug Ambrisko 					break;
28040d9a4ef3SDoug Ambrisko 			}
28050d9a4ef3SDoug Ambrisko 		}
28060d9a4ef3SDoug Ambrisko 		else
28070d9a4ef3SDoug Ambrisko 			break;
28080d9a4ef3SDoug Ambrisko 		if (syspd)
28090d9a4ef3SDoug Ambrisko 			error = mfi_syspd_disable(syspd);
28100d9a4ef3SDoug Ambrisko 		break;
28118ec5c98bSJohn Baldwin 	default:
28128ec5c98bSJohn Baldwin 		break;
28138ec5c98bSJohn Baldwin 	}
28148ec5c98bSJohn Baldwin 	return (error);
28158ec5c98bSJohn Baldwin }
28168ec5c98bSJohn Baldwin 
28178ec5c98bSJohn Baldwin /* Perform post-issue checks on commands from userland. */
28188ec5c98bSJohn Baldwin static void
mfi_check_command_post(struct mfi_softc * sc,struct mfi_command * cm)28198ec5c98bSJohn Baldwin mfi_check_command_post(struct mfi_softc *sc, struct mfi_command *cm)
28208ec5c98bSJohn Baldwin {
28218ec5c98bSJohn Baldwin 	struct mfi_disk *ld, *ldn;
28220d9a4ef3SDoug Ambrisko 	struct mfi_system_pd *syspd = NULL;
28230d9a4ef3SDoug Ambrisko 	uint16_t syspd_id;
28240d9a4ef3SDoug Ambrisko 	uint16_t *mbox;
28258ec5c98bSJohn Baldwin 
28268ec5c98bSJohn Baldwin 	switch (cm->cm_frame->dcmd.opcode) {
28278ec5c98bSJohn Baldwin 	case MFI_DCMD_LD_DELETE:
28288ec5c98bSJohn Baldwin 		TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link) {
28298ec5c98bSJohn Baldwin 			if (ld->ld_id == cm->cm_frame->dcmd.mbox[0])
28308ec5c98bSJohn Baldwin 				break;
28318ec5c98bSJohn Baldwin 		}
28328ec5c98bSJohn Baldwin 		KASSERT(ld != NULL, ("volume dissappeared"));
28338ec5c98bSJohn Baldwin 		if (cm->cm_frame->header.cmd_status == MFI_STAT_OK) {
28348ec5c98bSJohn Baldwin 			mtx_unlock(&sc->mfi_io_lock);
2835c6df6f53SWarner Losh 			bus_topo_lock();
28368ec5c98bSJohn Baldwin 			device_delete_child(sc->mfi_dev, ld->ld_dev);
2837c6df6f53SWarner Losh 			bus_topo_unlock();
28388ec5c98bSJohn Baldwin 			mtx_lock(&sc->mfi_io_lock);
28398ec5c98bSJohn Baldwin 		} else
28408ec5c98bSJohn Baldwin 			mfi_disk_enable(ld);
28418ec5c98bSJohn Baldwin 		break;
28428ec5c98bSJohn Baldwin 	case MFI_DCMD_CFG_CLEAR:
28438ec5c98bSJohn Baldwin 		if (cm->cm_frame->header.cmd_status == MFI_STAT_OK) {
28448ec5c98bSJohn Baldwin 			mtx_unlock(&sc->mfi_io_lock);
2845c6df6f53SWarner Losh 			bus_topo_lock();
28468ec5c98bSJohn Baldwin 			TAILQ_FOREACH_SAFE(ld, &sc->mfi_ld_tqh, ld_link, ldn) {
28478ec5c98bSJohn Baldwin 				device_delete_child(sc->mfi_dev, ld->ld_dev);
28488ec5c98bSJohn Baldwin 			}
2849c6df6f53SWarner Losh 			bus_topo_unlock();
28508ec5c98bSJohn Baldwin 			mtx_lock(&sc->mfi_io_lock);
28518ec5c98bSJohn Baldwin 		} else {
28528ec5c98bSJohn Baldwin 			TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link)
28538ec5c98bSJohn Baldwin 				mfi_disk_enable(ld);
28548ec5c98bSJohn Baldwin 		}
28558ec5c98bSJohn Baldwin 		break;
28568ec5c98bSJohn Baldwin 	case MFI_DCMD_CFG_ADD:
28578ec5c98bSJohn Baldwin 		mfi_ldprobe(sc);
28588ec5c98bSJohn Baldwin 		break;
2859fa1e6ef4SDoug Ambrisko 	case MFI_DCMD_CFG_FOREIGN_IMPORT:
2860fa1e6ef4SDoug Ambrisko 		mfi_ldprobe(sc);
2861fa1e6ef4SDoug Ambrisko 		break;
28620d9a4ef3SDoug Ambrisko 	case MFI_DCMD_PD_STATE_SET:
28630d9a4ef3SDoug Ambrisko 		mbox = (uint16_t *) cm->cm_frame->dcmd.mbox;
28640d9a4ef3SDoug Ambrisko 		syspd_id = mbox[0];
28650d9a4ef3SDoug Ambrisko 		if (mbox[2] == MFI_PD_STATE_UNCONFIGURED_GOOD) {
28660d9a4ef3SDoug Ambrisko 			TAILQ_FOREACH(syspd, &sc->mfi_syspd_tqh,pd_link) {
28670d9a4ef3SDoug Ambrisko 				if (syspd->pd_id == syspd_id)
28680d9a4ef3SDoug Ambrisko 					break;
28698ec5c98bSJohn Baldwin 			}
28708ec5c98bSJohn Baldwin 		}
28710d9a4ef3SDoug Ambrisko 		else
28720d9a4ef3SDoug Ambrisko 			break;
28730d9a4ef3SDoug Ambrisko 		/* If the transition fails then enable the syspd again */
28740d9a4ef3SDoug Ambrisko 		if (syspd && cm->cm_frame->header.cmd_status != MFI_STAT_OK)
28750d9a4ef3SDoug Ambrisko 			mfi_syspd_enable(syspd);
28760d9a4ef3SDoug Ambrisko 		break;
28770d9a4ef3SDoug Ambrisko 	}
28780d9a4ef3SDoug Ambrisko }
28790d9a4ef3SDoug Ambrisko 
288058ef3154SDoug Ambrisko static int
mfi_check_for_sscd(struct mfi_softc * sc,struct mfi_command * cm)288158ef3154SDoug Ambrisko mfi_check_for_sscd(struct mfi_softc *sc, struct mfi_command *cm)
28820d9a4ef3SDoug Ambrisko {
288358ef3154SDoug Ambrisko 	struct mfi_config_data *conf_data;
28840d9a4ef3SDoug Ambrisko 	struct mfi_command *ld_cm = NULL;
28850d9a4ef3SDoug Ambrisko 	struct mfi_ld_info *ld_info = NULL;
288658ef3154SDoug Ambrisko 	struct mfi_ld_config *ld;
288758ef3154SDoug Ambrisko 	char *p;
28880d9a4ef3SDoug Ambrisko 	int error = 0;
28890d9a4ef3SDoug Ambrisko 
289058ef3154SDoug Ambrisko 	conf_data = (struct mfi_config_data *)cm->cm_data;
289158ef3154SDoug Ambrisko 
289258ef3154SDoug Ambrisko 	if (cm->cm_frame->dcmd.opcode == MFI_DCMD_CFG_ADD) {
289358ef3154SDoug Ambrisko 		p = (char *)conf_data->array;
289458ef3154SDoug Ambrisko 		p += conf_data->array_size * conf_data->array_count;
289558ef3154SDoug Ambrisko 		ld = (struct mfi_ld_config *)p;
289658ef3154SDoug Ambrisko 		if (ld->params.isSSCD == 1)
28970d9a4ef3SDoug Ambrisko 			error = 1;
28980d9a4ef3SDoug Ambrisko 	} else if (cm->cm_frame->dcmd.opcode == MFI_DCMD_LD_DELETE) {
28990d9a4ef3SDoug Ambrisko 		error = mfi_dcmd_command (sc, &ld_cm, MFI_DCMD_LD_GET_INFO,
29000d9a4ef3SDoug Ambrisko 		    (void **)&ld_info, sizeof(*ld_info));
29010d9a4ef3SDoug Ambrisko 		if (error) {
29020d9a4ef3SDoug Ambrisko 			device_printf(sc->mfi_dev, "Failed to allocate"
29030d9a4ef3SDoug Ambrisko 			    "MFI_DCMD_LD_GET_INFO %d", error);
29040d9a4ef3SDoug Ambrisko 			if (ld_info)
29050d9a4ef3SDoug Ambrisko 				free(ld_info, M_MFIBUF);
29060d9a4ef3SDoug Ambrisko 			return 0;
29070d9a4ef3SDoug Ambrisko 		}
29080d9a4ef3SDoug Ambrisko 		ld_cm->cm_flags = MFI_CMD_DATAIN;
29090d9a4ef3SDoug Ambrisko 		ld_cm->cm_frame->dcmd.mbox[0]= cm->cm_frame->dcmd.mbox[0];
29100d9a4ef3SDoug Ambrisko 		ld_cm->cm_frame->header.target_id = cm->cm_frame->dcmd.mbox[0];
29110d9a4ef3SDoug Ambrisko 		if (mfi_wait_command(sc, ld_cm) != 0) {
29120d9a4ef3SDoug Ambrisko 			device_printf(sc->mfi_dev, "failed to get log drv\n");
29130d9a4ef3SDoug Ambrisko 			mfi_release_command(ld_cm);
29140d9a4ef3SDoug Ambrisko 			free(ld_info, M_MFIBUF);
29150d9a4ef3SDoug Ambrisko 			return 0;
29160d9a4ef3SDoug Ambrisko 		}
29170d9a4ef3SDoug Ambrisko 
29180d9a4ef3SDoug Ambrisko 		if (ld_cm->cm_frame->header.cmd_status != MFI_STAT_OK) {
29190d9a4ef3SDoug Ambrisko 			free(ld_info, M_MFIBUF);
29200d9a4ef3SDoug Ambrisko 			mfi_release_command(ld_cm);
29210d9a4ef3SDoug Ambrisko 			return 0;
29220d9a4ef3SDoug Ambrisko 		}
29230d9a4ef3SDoug Ambrisko 		else
29240d9a4ef3SDoug Ambrisko 			ld_info = (struct mfi_ld_info *)ld_cm->cm_private;
29250d9a4ef3SDoug Ambrisko 
29260d9a4ef3SDoug Ambrisko 		if (ld_info->ld_config.params.isSSCD == 1)
29270d9a4ef3SDoug Ambrisko 			error = 1;
29280d9a4ef3SDoug Ambrisko 
29290d9a4ef3SDoug Ambrisko 		mfi_release_command(ld_cm);
29300d9a4ef3SDoug Ambrisko 		free(ld_info, M_MFIBUF);
29310d9a4ef3SDoug Ambrisko 	}
29320d9a4ef3SDoug Ambrisko 	return error;
29330d9a4ef3SDoug Ambrisko }
29340d9a4ef3SDoug Ambrisko 
29350d9a4ef3SDoug Ambrisko static int
mfi_stp_cmd(struct mfi_softc * sc,struct mfi_command * cm,caddr_t arg)29360d9a4ef3SDoug Ambrisko mfi_stp_cmd(struct mfi_softc *sc, struct mfi_command *cm,caddr_t arg)
29370d9a4ef3SDoug Ambrisko {
29380d9a4ef3SDoug Ambrisko 	uint8_t i;
29390d9a4ef3SDoug Ambrisko 	struct mfi_ioc_packet *ioc;
29400d9a4ef3SDoug Ambrisko 	ioc = (struct mfi_ioc_packet *)arg;
29410d9a4ef3SDoug Ambrisko 	int sge_size, error;
29420d9a4ef3SDoug Ambrisko 	struct megasas_sge *kern_sge;
29430d9a4ef3SDoug Ambrisko 
29440d9a4ef3SDoug Ambrisko 	memset(sc->kbuff_arr, 0, sizeof(sc->kbuff_arr));
29450d9a4ef3SDoug Ambrisko 	kern_sge =(struct megasas_sge *) ((uintptr_t)cm->cm_frame + ioc->mfi_sgl_off);
29460d9a4ef3SDoug Ambrisko 	cm->cm_frame->header.sg_count = ioc->mfi_sge_count;
29470d9a4ef3SDoug Ambrisko 
29480d9a4ef3SDoug Ambrisko 	if (sizeof(bus_addr_t) == 8) {
29490d9a4ef3SDoug Ambrisko 		cm->cm_frame->header.flags |= MFI_FRAME_SGL64;
29500d9a4ef3SDoug Ambrisko 		cm->cm_extra_frames = 2;
29510d9a4ef3SDoug Ambrisko 		sge_size = sizeof(struct mfi_sg64);
29520d9a4ef3SDoug Ambrisko 	} else {
29530d9a4ef3SDoug Ambrisko 		cm->cm_extra_frames =  (cm->cm_total_frame_size - 1) / MFI_FRAME_SIZE;
29540d9a4ef3SDoug Ambrisko 		sge_size = sizeof(struct mfi_sg32);
29550d9a4ef3SDoug Ambrisko 	}
29560d9a4ef3SDoug Ambrisko 
29570d9a4ef3SDoug Ambrisko 	cm->cm_total_frame_size += (sge_size * ioc->mfi_sge_count);
2958a6ba0fd6SDoug Ambrisko 	for (i = 0; i < ioc->mfi_sge_count; i++) {
29590d9a4ef3SDoug Ambrisko 			if (bus_dma_tag_create( sc->mfi_parent_dmat,	/* parent */
29600d9a4ef3SDoug Ambrisko 			1, 0,			/* algnmnt, boundary */
29610d9a4ef3SDoug Ambrisko 			BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
29620d9a4ef3SDoug Ambrisko 			BUS_SPACE_MAXADDR,	/* highaddr */
29630d9a4ef3SDoug Ambrisko 			NULL, NULL,		/* filter, filterarg */
29640d9a4ef3SDoug Ambrisko 			ioc->mfi_sgl[i].iov_len,/* maxsize */
29650d9a4ef3SDoug Ambrisko 			2,			/* nsegments */
29660d9a4ef3SDoug Ambrisko 			ioc->mfi_sgl[i].iov_len,/* maxsegsize */
29670d9a4ef3SDoug Ambrisko 			BUS_DMA_ALLOCNOW,	/* flags */
29680d9a4ef3SDoug Ambrisko 			NULL, NULL,		/* lockfunc, lockarg */
29690d9a4ef3SDoug Ambrisko 			&sc->mfi_kbuff_arr_dmat[i])) {
2970a6ba0fd6SDoug Ambrisko 			device_printf(sc->mfi_dev,
2971a6ba0fd6SDoug Ambrisko 			    "Cannot allocate mfi_kbuff_arr_dmat tag\n");
29720d9a4ef3SDoug Ambrisko 			return (ENOMEM);
29730d9a4ef3SDoug Ambrisko 		}
29740d9a4ef3SDoug Ambrisko 
2975a6ba0fd6SDoug Ambrisko 		if (bus_dmamem_alloc(sc->mfi_kbuff_arr_dmat[i],
2976a6ba0fd6SDoug Ambrisko 		    (void **)&sc->kbuff_arr[i], BUS_DMA_NOWAIT,
2977a6ba0fd6SDoug Ambrisko 		    &sc->mfi_kbuff_arr_dmamap[i])) {
2978a6ba0fd6SDoug Ambrisko 			device_printf(sc->mfi_dev,
2979a6ba0fd6SDoug Ambrisko 			    "Cannot allocate mfi_kbuff_arr_dmamap memory\n");
29800d9a4ef3SDoug Ambrisko 			return (ENOMEM);
29810d9a4ef3SDoug Ambrisko 		}
29820d9a4ef3SDoug Ambrisko 
2983a6ba0fd6SDoug Ambrisko 		bus_dmamap_load(sc->mfi_kbuff_arr_dmat[i],
2984a6ba0fd6SDoug Ambrisko 		    sc->mfi_kbuff_arr_dmamap[i], sc->kbuff_arr[i],
2985a6ba0fd6SDoug Ambrisko 		    ioc->mfi_sgl[i].iov_len, mfi_addr_cb,
2986a6ba0fd6SDoug Ambrisko 		    &sc->mfi_kbuff_arr_busaddr[i], 0);
29870d9a4ef3SDoug Ambrisko 
29880d9a4ef3SDoug Ambrisko 		if (!sc->kbuff_arr[i]) {
2989a6ba0fd6SDoug Ambrisko 			device_printf(sc->mfi_dev,
2990a6ba0fd6SDoug Ambrisko 			    "Could not allocate memory for kbuff_arr info\n");
29910d9a4ef3SDoug Ambrisko 			return -1;
29920d9a4ef3SDoug Ambrisko 		}
29930d9a4ef3SDoug Ambrisko 		kern_sge[i].phys_addr = sc->mfi_kbuff_arr_busaddr[i];
29940d9a4ef3SDoug Ambrisko 		kern_sge[i].length = ioc->mfi_sgl[i].iov_len;
29950d9a4ef3SDoug Ambrisko 
29960d9a4ef3SDoug Ambrisko 		if (sizeof(bus_addr_t) == 8) {
2997a6ba0fd6SDoug Ambrisko 			cm->cm_frame->stp.sgl.sg64[i].addr =
2998a6ba0fd6SDoug Ambrisko 			    kern_sge[i].phys_addr;
2999a6ba0fd6SDoug Ambrisko 			cm->cm_frame->stp.sgl.sg64[i].len =
3000a6ba0fd6SDoug Ambrisko 			    ioc->mfi_sgl[i].iov_len;
30010d9a4ef3SDoug Ambrisko 		} else {
30020e47e251SXin LI 			cm->cm_frame->stp.sgl.sg32[i].addr =
3003a6ba0fd6SDoug Ambrisko 			    kern_sge[i].phys_addr;
3004a6ba0fd6SDoug Ambrisko 			cm->cm_frame->stp.sgl.sg32[i].len =
3005a6ba0fd6SDoug Ambrisko 			    ioc->mfi_sgl[i].iov_len;
30060d9a4ef3SDoug Ambrisko 		}
30070d9a4ef3SDoug Ambrisko 
30080d9a4ef3SDoug Ambrisko 		error = copyin(ioc->mfi_sgl[i].iov_base,
30090d9a4ef3SDoug Ambrisko 		    sc->kbuff_arr[i],
30100d9a4ef3SDoug Ambrisko 		    ioc->mfi_sgl[i].iov_len);
3011a6ba0fd6SDoug Ambrisko 		if (error != 0) {
3012a6ba0fd6SDoug Ambrisko 			device_printf(sc->mfi_dev, "Copy in failed\n");
30130d9a4ef3SDoug Ambrisko 			return error;
30140d9a4ef3SDoug Ambrisko 		}
30150d9a4ef3SDoug Ambrisko 	}
30160d9a4ef3SDoug Ambrisko 
30170d9a4ef3SDoug Ambrisko 	cm->cm_flags |=MFI_CMD_MAPPED;
30180d9a4ef3SDoug Ambrisko 	return 0;
30190d9a4ef3SDoug Ambrisko }
30208ec5c98bSJohn Baldwin 
30218ec5c98bSJohn Baldwin static int
mfi_user_command(struct mfi_softc * sc,struct mfi_ioc_passthru * ioc)30220929b669SScott Long mfi_user_command(struct mfi_softc *sc, struct mfi_ioc_passthru *ioc)
30230929b669SScott Long {
30240929b669SScott Long 	struct mfi_command *cm;
30250929b669SScott Long 	struct mfi_dcmd_frame *dcmd;
30260929b669SScott Long 	void *ioc_buf = NULL;
30270929b669SScott Long 	uint32_t context;
30280929b669SScott Long 	int error = 0, locked;
30290929b669SScott Long 
30300929b669SScott Long 	if (ioc->buf_size > 0) {
3031da146236SJohn Baldwin 		if (ioc->buf_size > 1024 * 1024)
30320929b669SScott Long 			return (ENOMEM);
3033da146236SJohn Baldwin 		ioc_buf = malloc(ioc->buf_size, M_MFIBUF, M_WAITOK);
30340929b669SScott Long 		error = copyin(ioc->buf, ioc_buf, ioc->buf_size);
30350929b669SScott Long 		if (error) {
30360929b669SScott Long 			device_printf(sc->mfi_dev, "failed to copyin\n");
30370929b669SScott Long 			free(ioc_buf, M_MFIBUF);
30380929b669SScott Long 			return (error);
30390929b669SScott Long 		}
30400929b669SScott Long 	}
30410929b669SScott Long 
30420929b669SScott Long 	locked = mfi_config_lock(sc, ioc->ioc_frame.opcode);
30430929b669SScott Long 
30440929b669SScott Long 	mtx_lock(&sc->mfi_io_lock);
30450929b669SScott Long 	while ((cm = mfi_dequeue_free(sc)) == NULL)
30460929b669SScott Long 		msleep(mfi_user_command, &sc->mfi_io_lock, 0, "mfiioc", hz);
30470929b669SScott Long 
30480929b669SScott Long 	/* Save context for later */
30490929b669SScott Long 	context = cm->cm_frame->header.context;
30500929b669SScott Long 
30510929b669SScott Long 	dcmd = &cm->cm_frame->dcmd;
30520929b669SScott Long 	bcopy(&ioc->ioc_frame, dcmd, sizeof(struct mfi_dcmd_frame));
30530929b669SScott Long 
30540929b669SScott Long 	cm->cm_sg = &dcmd->sgl;
30550929b669SScott Long 	cm->cm_total_frame_size = MFI_DCMD_FRAME_SIZE;
30560929b669SScott Long 	cm->cm_data = ioc_buf;
30570929b669SScott Long 	cm->cm_len = ioc->buf_size;
30580929b669SScott Long 
30590929b669SScott Long 	/* restore context */
30600929b669SScott Long 	cm->cm_frame->header.context = context;
30610929b669SScott Long 
30620929b669SScott Long 	/* Cheat since we don't know if we're writing or reading */
30630929b669SScott Long 	cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_DATAOUT;
30640929b669SScott Long 
30650929b669SScott Long 	error = mfi_check_command_pre(sc, cm);
30660929b669SScott Long 	if (error)
30670929b669SScott Long 		goto out;
30680929b669SScott Long 
30690929b669SScott Long 	error = mfi_wait_command(sc, cm);
30700929b669SScott Long 	if (error) {
30710929b669SScott Long 		device_printf(sc->mfi_dev, "ioctl failed %d\n", error);
30720929b669SScott Long 		goto out;
30730929b669SScott Long 	}
30740929b669SScott Long 	bcopy(dcmd, &ioc->ioc_frame, sizeof(struct mfi_dcmd_frame));
30750929b669SScott Long 	mfi_check_command_post(sc, cm);
30760929b669SScott Long out:
30770929b669SScott Long 	mfi_release_command(cm);
30780929b669SScott Long 	mtx_unlock(&sc->mfi_io_lock);
30790929b669SScott Long 	mfi_config_unlock(sc, locked);
30800929b669SScott Long 	if (ioc->buf_size > 0)
30810929b669SScott Long 		error = copyout(ioc_buf, ioc->buf, ioc->buf_size);
30820929b669SScott Long 	if (ioc_buf)
30830929b669SScott Long 		free(ioc_buf, M_MFIBUF);
30840929b669SScott Long 	return (error);
30850929b669SScott Long }
30860929b669SScott Long 
30870929b669SScott Long #define	PTRIN(p)		((void *)(uintptr_t)(p))
30880929b669SScott Long 
30890929b669SScott Long static int
mfi_ioctl(struct cdev * dev,u_long cmd,caddr_t arg,int flag,struct thread * td)309000b4e54aSWarner Losh mfi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
30912e21a3efSScott Long {
30922e21a3efSScott Long 	struct mfi_softc *sc;
30932e21a3efSScott Long 	union mfi_statrequest *ms;
3094c2be47f2SDoug Ambrisko 	struct mfi_ioc_packet *ioc;
3095a6ba0fd6SDoug Ambrisko #ifdef COMPAT_FREEBSD32
309646fb79eaSDoug Ambrisko 	struct mfi_ioc_packet32 *ioc32;
309746fb79eaSDoug Ambrisko #endif
3098c2be47f2SDoug Ambrisko 	struct mfi_ioc_aen *aen;
3099c2be47f2SDoug Ambrisko 	struct mfi_command *cm = NULL;
31000d9a4ef3SDoug Ambrisko 	uint32_t context = 0;
3101fa1e6ef4SDoug Ambrisko 	union mfi_sense_ptr sense_ptr;
31020d9a4ef3SDoug Ambrisko 	uint8_t *data = NULL, *temp, *addr, skip_pre_post = 0;
31035f8ad41cSAlexander Motin 	size_t len;
31040d9a4ef3SDoug Ambrisko 	int i, res;
31050929b669SScott Long 	struct mfi_ioc_passthru *iop = (struct mfi_ioc_passthru *)arg;
3106a6ba0fd6SDoug Ambrisko #ifdef COMPAT_FREEBSD32
31070929b669SScott Long 	struct mfi_ioc_passthru32 *iop32 = (struct mfi_ioc_passthru32 *)arg;
31080929b669SScott Long 	struct mfi_ioc_passthru iop_swab;
31090929b669SScott Long #endif
31108ec5c98bSJohn Baldwin 	int error, locked;
31112e21a3efSScott Long 	sc = dev->si_drv1;
31122e21a3efSScott Long 	error = 0;
31132e21a3efSScott Long 
31140d9a4ef3SDoug Ambrisko 	if (sc->adpreset)
31150d9a4ef3SDoug Ambrisko 		return EBUSY;
31160d9a4ef3SDoug Ambrisko 
31170d9a4ef3SDoug Ambrisko 	if (sc->hw_crit_error)
31180d9a4ef3SDoug Ambrisko 		return EBUSY;
31190d9a4ef3SDoug Ambrisko 
31200d9a4ef3SDoug Ambrisko 	if (sc->issuepend_done == 0)
31210d9a4ef3SDoug Ambrisko 		return EBUSY;
31220d9a4ef3SDoug Ambrisko 
31232e21a3efSScott Long 	switch (cmd) {
31242e21a3efSScott Long 	case MFIIO_STATS:
31252e21a3efSScott Long 		ms = (union mfi_statrequest *)arg;
31262e21a3efSScott Long 		switch (ms->ms_item) {
31272e21a3efSScott Long 		case MFIQ_FREE:
31282e21a3efSScott Long 		case MFIQ_BIO:
31292e21a3efSScott Long 		case MFIQ_READY:
31302e21a3efSScott Long 		case MFIQ_BUSY:
31312e21a3efSScott Long 			bcopy(&sc->mfi_qstat[ms->ms_item], &ms->ms_qstat,
31322e21a3efSScott Long 			    sizeof(struct mfi_qstat));
31332e21a3efSScott Long 			break;
31342e21a3efSScott Long 		default:
3135741367d5SDoug Ambrisko 			error = ENOIOCTL;
31362e21a3efSScott Long 			break;
31372e21a3efSScott Long 		}
31382e21a3efSScott Long 		break;
3139ddfae47bSScott Long 	case MFIIO_QUERY_DISK:
3140ddfae47bSScott Long 	{
3141ddfae47bSScott Long 		struct mfi_query_disk *qd;
3142ddfae47bSScott Long 		struct mfi_disk *ld;
3143ddfae47bSScott Long 
3144ddfae47bSScott Long 		qd = (struct mfi_query_disk *)arg;
3145ddfae47bSScott Long 		mtx_lock(&sc->mfi_io_lock);
3146ddfae47bSScott Long 		TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link) {
3147ddfae47bSScott Long 			if (ld->ld_id == qd->array_id)
3148ddfae47bSScott Long 				break;
3149ddfae47bSScott Long 		}
3150ddfae47bSScott Long 		if (ld == NULL) {
3151ddfae47bSScott Long 			qd->present = 0;
3152ddfae47bSScott Long 			mtx_unlock(&sc->mfi_io_lock);
3153ddfae47bSScott Long 			return (0);
3154ddfae47bSScott Long 		}
3155ddfae47bSScott Long 		qd->present = 1;
3156ddfae47bSScott Long 		if (ld->ld_flags & MFI_DISK_FLAGS_OPEN)
3157ddfae47bSScott Long 			qd->open = 1;
3158ddfae47bSScott Long 		bzero(qd->devname, SPECNAMELEN + 1);
3159ddfae47bSScott Long 		snprintf(qd->devname, SPECNAMELEN, "mfid%d", ld->ld_unit);
3160ddfae47bSScott Long 		mtx_unlock(&sc->mfi_io_lock);
3161ddfae47bSScott Long 		break;
3162ddfae47bSScott Long 	}
3163c2be47f2SDoug Ambrisko 	case MFI_CMD:
3164a6ba0fd6SDoug Ambrisko #ifdef COMPAT_FREEBSD32
316546fb79eaSDoug Ambrisko 	case MFI_CMD32:
316646fb79eaSDoug Ambrisko #endif
3167a355f43eSDoug Ambrisko 		{
3168a355f43eSDoug Ambrisko 		devclass_t devclass;
3169c2be47f2SDoug Ambrisko 		ioc = (struct mfi_ioc_packet *)arg;
3170a355f43eSDoug Ambrisko 		int adapter;
3171c2be47f2SDoug Ambrisko 
3172a355f43eSDoug Ambrisko 		adapter = ioc->mfi_adapter_no;
3173a355f43eSDoug Ambrisko 		if (device_get_unit(sc->mfi_dev) == 0 && adapter != 0) {
3174a355f43eSDoug Ambrisko 			devclass = devclass_find("mfi");
3175a355f43eSDoug Ambrisko 			sc = devclass_get_softc(devclass, adapter);
3176a355f43eSDoug Ambrisko 		}
3177c2be47f2SDoug Ambrisko 		mtx_lock(&sc->mfi_io_lock);
3178c2be47f2SDoug Ambrisko 		if ((cm = mfi_dequeue_free(sc)) == NULL) {
3179c2be47f2SDoug Ambrisko 			mtx_unlock(&sc->mfi_io_lock);
3180c2be47f2SDoug Ambrisko 			return (EBUSY);
3181c2be47f2SDoug Ambrisko 		}
3182c2be47f2SDoug Ambrisko 		mtx_unlock(&sc->mfi_io_lock);
31838ec5c98bSJohn Baldwin 		locked = 0;
3184c2be47f2SDoug Ambrisko 
3185c2be47f2SDoug Ambrisko 		/*
3186c2be47f2SDoug Ambrisko 		 * save off original context since copying from user
3187c2be47f2SDoug Ambrisko 		 * will clobber some data
3188c2be47f2SDoug Ambrisko 		 */
3189c2be47f2SDoug Ambrisko 		context = cm->cm_frame->header.context;
31900d9a4ef3SDoug Ambrisko 		cm->cm_frame->header.context = cm->cm_index;
3191c2be47f2SDoug Ambrisko 
3192812819c7SDoug Ambrisko 		bcopy(ioc->mfi_frame.raw, cm->cm_frame,
31930d9a4ef3SDoug Ambrisko 		    2 * MEGAMFI_FRAME_SIZE);
3194fa1e6ef4SDoug Ambrisko 		cm->cm_total_frame_size = (sizeof(union mfi_sgl)
3195fa1e6ef4SDoug Ambrisko 		    * ioc->mfi_sge_count) + ioc->mfi_sgl_off;
31960d9a4ef3SDoug Ambrisko 		cm->cm_frame->header.scsi_status = 0;
31970d9a4ef3SDoug Ambrisko 		cm->cm_frame->header.pad0 = 0;
319804697de9SDoug Ambrisko 		if (ioc->mfi_sge_count) {
3199c2be47f2SDoug Ambrisko 			cm->cm_sg =
3200812819c7SDoug Ambrisko 			    (union mfi_sgl *)&cm->cm_frame->bytes[ioc->mfi_sgl_off];
320104697de9SDoug Ambrisko 		}
320204697de9SDoug Ambrisko 		cm->cm_flags = 0;
320304697de9SDoug Ambrisko 		if (cm->cm_frame->header.flags & MFI_FRAME_DATAIN)
320404697de9SDoug Ambrisko 			cm->cm_flags |= MFI_CMD_DATAIN;
320504697de9SDoug Ambrisko 		if (cm->cm_frame->header.flags & MFI_FRAME_DATAOUT)
320604697de9SDoug Ambrisko 			cm->cm_flags |= MFI_CMD_DATAOUT;
320704697de9SDoug Ambrisko 		/* Legacy app shim */
320804697de9SDoug Ambrisko 		if (cm->cm_flags == 0)
320904697de9SDoug Ambrisko 			cm->cm_flags |= MFI_CMD_DATAIN | MFI_CMD_DATAOUT;
3210c2be47f2SDoug Ambrisko 		cm->cm_len = cm->cm_frame->header.data_len;
32115f8ad41cSAlexander Motin 		if (cm->cm_frame->header.cmd == MFI_CMD_STP) {
3212a6ba0fd6SDoug Ambrisko #ifdef COMPAT_FREEBSD32
32135f8ad41cSAlexander Motin 			if (cmd == MFI_CMD) {
32145f8ad41cSAlexander Motin #endif
32155f8ad41cSAlexander Motin 				/* Native */
32165f8ad41cSAlexander Motin 				cm->cm_stp_len = ioc->mfi_sgl[0].iov_len;
3217a6ba0fd6SDoug Ambrisko #ifdef COMPAT_FREEBSD32
32185f8ad41cSAlexander Motin 			} else {
32195f8ad41cSAlexander Motin 				/* 32bit on 64bit */
32205f8ad41cSAlexander Motin 				ioc32 = (struct mfi_ioc_packet32 *)ioc;
32215f8ad41cSAlexander Motin 				cm->cm_stp_len = ioc32->mfi_sgl[0].iov_len;
32225f8ad41cSAlexander Motin 			}
32235f8ad41cSAlexander Motin #endif
32245f8ad41cSAlexander Motin 			cm->cm_len += cm->cm_stp_len;
32255f8ad41cSAlexander Motin 		}
3226fa1e6ef4SDoug Ambrisko 		if (cm->cm_len &&
3227fa1e6ef4SDoug Ambrisko 		    (cm->cm_flags & (MFI_CMD_DATAIN | MFI_CMD_DATAOUT))) {
3228c2be47f2SDoug Ambrisko 			cm->cm_data = data = malloc(cm->cm_len, M_MFIBUF,
3229c2be47f2SDoug Ambrisko 			    M_WAITOK | M_ZERO);
323004697de9SDoug Ambrisko 		} else {
323104697de9SDoug Ambrisko 			cm->cm_data = 0;
323204697de9SDoug Ambrisko 		}
3233c2be47f2SDoug Ambrisko 
3234c2be47f2SDoug Ambrisko 		/* restore header context */
3235c2be47f2SDoug Ambrisko 		cm->cm_frame->header.context = context;
3236c2be47f2SDoug Ambrisko 
32370d9a4ef3SDoug Ambrisko 		if (cm->cm_frame->header.cmd == MFI_CMD_STP) {
32380d9a4ef3SDoug Ambrisko 			res = mfi_stp_cmd(sc, cm, arg);
32390d9a4ef3SDoug Ambrisko 			if (res != 0)
32400d9a4ef3SDoug Ambrisko 				goto out;
32410d9a4ef3SDoug Ambrisko 		} else {
3242c2be47f2SDoug Ambrisko 			temp = data;
32435f8ad41cSAlexander Motin 			if ((cm->cm_flags & MFI_CMD_DATAOUT) ||
32445f8ad41cSAlexander Motin 			    (cm->cm_frame->header.cmd == MFI_CMD_STP)) {
3245812819c7SDoug Ambrisko 				for (i = 0; i < ioc->mfi_sge_count; i++) {
3246a6ba0fd6SDoug Ambrisko #ifdef COMPAT_FREEBSD32
324746fb79eaSDoug Ambrisko 					if (cmd == MFI_CMD) {
324846fb79eaSDoug Ambrisko #endif
32495f8ad41cSAlexander Motin 						/* Native */
32505f8ad41cSAlexander Motin 						addr = ioc->mfi_sgl[i].iov_base;
32515f8ad41cSAlexander Motin 						len = ioc->mfi_sgl[i].iov_len;
3252a6ba0fd6SDoug Ambrisko #ifdef COMPAT_FREEBSD32
32535f8ad41cSAlexander Motin 					} else {
32545f8ad41cSAlexander Motin 						/* 32bit on 64bit */
32555f8ad41cSAlexander Motin 						ioc32 = (struct mfi_ioc_packet32 *)ioc;
32565f8ad41cSAlexander Motin 						addr = PTRIN(ioc32->mfi_sgl[i].iov_base);
32575f8ad41cSAlexander Motin 						len = ioc32->mfi_sgl[i].iov_len;
32585f8ad41cSAlexander Motin 					}
32595f8ad41cSAlexander Motin #endif
32605f8ad41cSAlexander Motin 					error = copyin(addr, temp, len);
3261c2be47f2SDoug Ambrisko 					if (error != 0) {
3262c2be47f2SDoug Ambrisko 						device_printf(sc->mfi_dev,
3263812819c7SDoug Ambrisko 						    "Copy in failed\n");
3264c2be47f2SDoug Ambrisko 						goto out;
3265c2be47f2SDoug Ambrisko 					}
32665f8ad41cSAlexander Motin 					temp = &temp[len];
3267c2be47f2SDoug Ambrisko 				}
326804697de9SDoug Ambrisko 			}
32690a158415SDoug Ambrisko 		}
32700a158415SDoug Ambrisko 
3271c2be47f2SDoug Ambrisko 		if (cm->cm_frame->header.cmd == MFI_CMD_DCMD)
3272a6ba0fd6SDoug Ambrisko 			locked = mfi_config_lock(sc,
3273a6ba0fd6SDoug Ambrisko 			     cm->cm_frame->dcmd.opcode);
3274c2be47f2SDoug Ambrisko 
3275c2be47f2SDoug Ambrisko 		if (cm->cm_frame->header.cmd == MFI_CMD_PD_SCSI_IO) {
3276a6ba0fd6SDoug Ambrisko 			cm->cm_frame->pass.sense_addr_lo =
3277a6ba0fd6SDoug Ambrisko 			    (uint32_t)cm->cm_sense_busaddr;
3278a6ba0fd6SDoug Ambrisko 			cm->cm_frame->pass.sense_addr_hi =
3279a6ba0fd6SDoug Ambrisko 			    (uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32);
32808ec5c98bSJohn Baldwin 		}
3281c2be47f2SDoug Ambrisko 		mtx_lock(&sc->mfi_io_lock);
32820d9a4ef3SDoug Ambrisko 		skip_pre_post = mfi_check_for_sscd (sc, cm);
32830d9a4ef3SDoug Ambrisko 		if (!skip_pre_post) {
32848ec5c98bSJohn Baldwin 			error = mfi_check_command_pre(sc, cm);
32858ec5c98bSJohn Baldwin 			if (error) {
32868ec5c98bSJohn Baldwin 				mtx_unlock(&sc->mfi_io_lock);
32878ec5c98bSJohn Baldwin 				goto out;
32888ec5c98bSJohn Baldwin 			}
32890d9a4ef3SDoug Ambrisko 		}
32905be25877SDoug Ambrisko 		if ((error = mfi_wait_command(sc, cm)) != 0) {
3291c2be47f2SDoug Ambrisko 			device_printf(sc->mfi_dev,
3292812819c7SDoug Ambrisko 			    "Controller polled failed\n");
3293c2be47f2SDoug Ambrisko 			mtx_unlock(&sc->mfi_io_lock);
3294c2be47f2SDoug Ambrisko 			goto out;
3295c2be47f2SDoug Ambrisko 		}
32960d9a4ef3SDoug Ambrisko 		if (!skip_pre_post) {
32978ec5c98bSJohn Baldwin 			mfi_check_command_post(sc, cm);
32980d9a4ef3SDoug Ambrisko 		}
3299c2be47f2SDoug Ambrisko 		mtx_unlock(&sc->mfi_io_lock);
3300c2be47f2SDoug Ambrisko 
33010d9a4ef3SDoug Ambrisko 		if (cm->cm_frame->header.cmd != MFI_CMD_STP) {
3302c2be47f2SDoug Ambrisko 			temp = data;
33035f8ad41cSAlexander Motin 			if ((cm->cm_flags & MFI_CMD_DATAIN) ||
33045f8ad41cSAlexander Motin 			    (cm->cm_frame->header.cmd == MFI_CMD_STP)) {
3305812819c7SDoug Ambrisko 				for (i = 0; i < ioc->mfi_sge_count; i++) {
3306a6ba0fd6SDoug Ambrisko #ifdef COMPAT_FREEBSD32
330746fb79eaSDoug Ambrisko 					if (cmd == MFI_CMD) {
330846fb79eaSDoug Ambrisko #endif
33095f8ad41cSAlexander Motin 						/* Native */
33105f8ad41cSAlexander Motin 						addr = ioc->mfi_sgl[i].iov_base;
33115f8ad41cSAlexander Motin 						len = ioc->mfi_sgl[i].iov_len;
3312a6ba0fd6SDoug Ambrisko #ifdef COMPAT_FREEBSD32
33135f8ad41cSAlexander Motin 					} else {
33145f8ad41cSAlexander Motin 						/* 32bit on 64bit */
33155f8ad41cSAlexander Motin 						ioc32 = (struct mfi_ioc_packet32 *)ioc;
33165f8ad41cSAlexander Motin 						addr = PTRIN(ioc32->mfi_sgl[i].iov_base);
33175f8ad41cSAlexander Motin 						len = ioc32->mfi_sgl[i].iov_len;
33185f8ad41cSAlexander Motin 					}
33195f8ad41cSAlexander Motin #endif
33205f8ad41cSAlexander Motin 					error = copyout(temp, addr, len);
3321c2be47f2SDoug Ambrisko 					if (error != 0) {
3322c2be47f2SDoug Ambrisko 						device_printf(sc->mfi_dev,
3323812819c7SDoug Ambrisko 						    "Copy out failed\n");
3324c2be47f2SDoug Ambrisko 						goto out;
3325c2be47f2SDoug Ambrisko 					}
33265f8ad41cSAlexander Motin 					temp = &temp[len];
3327c2be47f2SDoug Ambrisko 				}
332804697de9SDoug Ambrisko 			}
33290d9a4ef3SDoug Ambrisko 		}
3330c2be47f2SDoug Ambrisko 
3331812819c7SDoug Ambrisko 		if (ioc->mfi_sense_len) {
3332fa1e6ef4SDoug Ambrisko 			/* get user-space sense ptr then copy out sense */
3333dc079616SBjoern A. Zeeb 			bcopy(&ioc->mfi_frame.raw[ioc->mfi_sense_off],
3334fa1e6ef4SDoug Ambrisko 			    &sense_ptr.sense_ptr_data[0],
3335fa1e6ef4SDoug Ambrisko 			    sizeof(sense_ptr.sense_ptr_data));
3336a6ba0fd6SDoug Ambrisko #ifdef COMPAT_FREEBSD32
3337b89b182cSDoug Ambrisko 			if (cmd != MFI_CMD) {
3338b89b182cSDoug Ambrisko 				/*
3339b89b182cSDoug Ambrisko 				 * not 64bit native so zero out any address
3340b89b182cSDoug Ambrisko 				 * over 32bit */
33414254ef59SDoug Ambrisko 				sense_ptr.addr.high = 0;
3342b89b182cSDoug Ambrisko 			}
3343b89b182cSDoug Ambrisko #endif
3344fa1e6ef4SDoug Ambrisko 			error = copyout(cm->cm_sense, sense_ptr.user_space,
3345812819c7SDoug Ambrisko 			    ioc->mfi_sense_len);
3346c2be47f2SDoug Ambrisko 			if (error != 0) {
3347c2be47f2SDoug Ambrisko 				device_printf(sc->mfi_dev,
3348812819c7SDoug Ambrisko 				    "Copy out failed\n");
3349c2be47f2SDoug Ambrisko 				goto out;
3350c2be47f2SDoug Ambrisko 			}
3351c2be47f2SDoug Ambrisko 		}
3352c2be47f2SDoug Ambrisko 
3353812819c7SDoug Ambrisko 		ioc->mfi_frame.hdr.cmd_status = cm->cm_frame->header.cmd_status;
3354c2be47f2SDoug Ambrisko out:
33558ec5c98bSJohn Baldwin 		mfi_config_unlock(sc, locked);
3356c2be47f2SDoug Ambrisko 		if (data)
3357c2be47f2SDoug Ambrisko 			free(data, M_MFIBUF);
3358a6ba0fd6SDoug Ambrisko 		if (cm->cm_frame->header.cmd == MFI_CMD_STP) {
3359a6ba0fd6SDoug Ambrisko 			for (i = 0; i < 2; i++) {
3360a6ba0fd6SDoug Ambrisko 				if (sc->kbuff_arr[i]) {
336127dc50b0SJohn Baldwin 					if (sc->mfi_kbuff_arr_busaddr[i] != 0)
3362a6ba0fd6SDoug Ambrisko 						bus_dmamap_unload(
3363a6ba0fd6SDoug Ambrisko 						    sc->mfi_kbuff_arr_dmat[i],
3364a6ba0fd6SDoug Ambrisko 						    sc->mfi_kbuff_arr_dmamap[i]
3365a6ba0fd6SDoug Ambrisko 						    );
33660d9a4ef3SDoug Ambrisko 					if (sc->kbuff_arr[i] != NULL)
3367a6ba0fd6SDoug Ambrisko 						bus_dmamem_free(
3368a6ba0fd6SDoug Ambrisko 						    sc->mfi_kbuff_arr_dmat[i],
3369a6ba0fd6SDoug Ambrisko 						    sc->kbuff_arr[i],
3370a6ba0fd6SDoug Ambrisko 						    sc->mfi_kbuff_arr_dmamap[i]
3371a6ba0fd6SDoug Ambrisko 						    );
33720d9a4ef3SDoug Ambrisko 					if (sc->mfi_kbuff_arr_dmat[i] != NULL)
3373a6ba0fd6SDoug Ambrisko 						bus_dma_tag_destroy(
3374a6ba0fd6SDoug Ambrisko 						    sc->mfi_kbuff_arr_dmat[i]);
33750d9a4ef3SDoug Ambrisko 				}
33760d9a4ef3SDoug Ambrisko 			}
33770d9a4ef3SDoug Ambrisko 		}
3378c2be47f2SDoug Ambrisko 		if (cm) {
3379c2be47f2SDoug Ambrisko 			mtx_lock(&sc->mfi_io_lock);
3380c2be47f2SDoug Ambrisko 			mfi_release_command(cm);
3381c2be47f2SDoug Ambrisko 			mtx_unlock(&sc->mfi_io_lock);
3382c2be47f2SDoug Ambrisko 		}
3383c2be47f2SDoug Ambrisko 
3384c2be47f2SDoug Ambrisko 		break;
3385a355f43eSDoug Ambrisko 		}
3386c2be47f2SDoug Ambrisko 	case MFI_SET_AEN:
3387c2be47f2SDoug Ambrisko 		aen = (struct mfi_ioc_aen *)arg;
338808c89430SSteven Hartland 		mtx_lock(&sc->mfi_io_lock);
3389c2be47f2SDoug Ambrisko 		error = mfi_aen_register(sc, aen->aen_seq_num,
3390c2be47f2SDoug Ambrisko 		    aen->aen_class_locale);
339108c89430SSteven Hartland 		mtx_unlock(&sc->mfi_io_lock);
3392c2be47f2SDoug Ambrisko 
3393c2be47f2SDoug Ambrisko 		break;
3394c2be47f2SDoug Ambrisko 	case MFI_LINUX_CMD_2: /* Firmware Linux ioctl shim */
3395741367d5SDoug Ambrisko 		{
3396741367d5SDoug Ambrisko 			devclass_t devclass;
3397741367d5SDoug Ambrisko 			struct mfi_linux_ioc_packet l_ioc;
3398741367d5SDoug Ambrisko 			int adapter;
3399741367d5SDoug Ambrisko 
3400741367d5SDoug Ambrisko 			devclass = devclass_find("mfi");
3401741367d5SDoug Ambrisko 			if (devclass == NULL)
3402741367d5SDoug Ambrisko 				return (ENOENT);
3403741367d5SDoug Ambrisko 
3404741367d5SDoug Ambrisko 			error = copyin(arg, &l_ioc, sizeof(l_ioc));
3405741367d5SDoug Ambrisko 			if (error)
3406741367d5SDoug Ambrisko 				return (error);
3407741367d5SDoug Ambrisko 			adapter = l_ioc.lioc_adapter_no;
3408741367d5SDoug Ambrisko 			sc = devclass_get_softc(devclass, adapter);
3409741367d5SDoug Ambrisko 			if (sc == NULL)
3410741367d5SDoug Ambrisko 				return (ENOENT);
3411741367d5SDoug Ambrisko 			return (mfi_linux_ioctl_int(sc->mfi_cdev,
3412741367d5SDoug Ambrisko 			    cmd, arg, flag, td));
3413741367d5SDoug Ambrisko 			break;
3414741367d5SDoug Ambrisko 		}
3415c2be47f2SDoug Ambrisko 	case MFI_LINUX_SET_AEN_2: /* AEN Linux ioctl shim */
3416741367d5SDoug Ambrisko 		{
3417741367d5SDoug Ambrisko 			devclass_t devclass;
3418741367d5SDoug Ambrisko 			struct mfi_linux_ioc_aen l_aen;
3419741367d5SDoug Ambrisko 			int adapter;
3420741367d5SDoug Ambrisko 
3421741367d5SDoug Ambrisko 			devclass = devclass_find("mfi");
3422741367d5SDoug Ambrisko 			if (devclass == NULL)
3423741367d5SDoug Ambrisko 				return (ENOENT);
3424741367d5SDoug Ambrisko 
3425741367d5SDoug Ambrisko 			error = copyin(arg, &l_aen, sizeof(l_aen));
3426741367d5SDoug Ambrisko 			if (error)
3427741367d5SDoug Ambrisko 				return (error);
3428741367d5SDoug Ambrisko 			adapter = l_aen.laen_adapter_no;
3429741367d5SDoug Ambrisko 			sc = devclass_get_softc(devclass, adapter);
3430741367d5SDoug Ambrisko 			if (sc == NULL)
3431741367d5SDoug Ambrisko 				return (ENOENT);
3432741367d5SDoug Ambrisko 			return (mfi_linux_ioctl_int(sc->mfi_cdev,
3433741367d5SDoug Ambrisko 			    cmd, arg, flag, td));
3434741367d5SDoug Ambrisko 			break;
3435741367d5SDoug Ambrisko 		}
3436a6ba0fd6SDoug Ambrisko #ifdef COMPAT_FREEBSD32
34370929b669SScott Long 	case MFIIO_PASSTHRU32:
3438da146236SJohn Baldwin 		if (!SV_CURPROC_FLAG(SV_ILP32)) {
3439da146236SJohn Baldwin 			error = ENOTTY;
3440da146236SJohn Baldwin 			break;
3441da146236SJohn Baldwin 		}
34420929b669SScott Long 		iop_swab.ioc_frame	= iop32->ioc_frame;
34430929b669SScott Long 		iop_swab.buf_size	= iop32->buf_size;
34440929b669SScott Long 		iop_swab.buf		= PTRIN(iop32->buf);
34450929b669SScott Long 		iop			= &iop_swab;
34460929b669SScott Long 		/* FALLTHROUGH */
34470929b669SScott Long #endif
34480929b669SScott Long 	case MFIIO_PASSTHRU:
34490929b669SScott Long 		error = mfi_user_command(sc, iop);
3450a6ba0fd6SDoug Ambrisko #ifdef COMPAT_FREEBSD32
34510929b669SScott Long 		if (cmd == MFIIO_PASSTHRU32)
34520929b669SScott Long 			iop32->ioc_frame = iop_swab.ioc_frame;
34530929b669SScott Long #endif
34540929b669SScott Long 		break;
34552e21a3efSScott Long 	default:
3456441f6d5dSScott Long 		device_printf(sc->mfi_dev, "IOCTL 0x%lx not handled\n", cmd);
3457da146236SJohn Baldwin 		error = ENOTTY;
34582e21a3efSScott Long 		break;
34592e21a3efSScott Long 	}
34602e21a3efSScott Long 
34612e21a3efSScott Long 	return (error);
34622e21a3efSScott Long }
3463741367d5SDoug Ambrisko 
3464741367d5SDoug Ambrisko static int
mfi_linux_ioctl_int(struct cdev * dev,u_long cmd,caddr_t arg,int flag,struct thread * td)346500b4e54aSWarner Losh mfi_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
3466741367d5SDoug Ambrisko {
3467741367d5SDoug Ambrisko 	struct mfi_softc *sc;
3468741367d5SDoug Ambrisko 	struct mfi_linux_ioc_packet l_ioc;
3469741367d5SDoug Ambrisko 	struct mfi_linux_ioc_aen l_aen;
3470741367d5SDoug Ambrisko 	struct mfi_command *cm = NULL;
3471741367d5SDoug Ambrisko 	struct mfi_aen *mfi_aen_entry;
3472fa1e6ef4SDoug Ambrisko 	union mfi_sense_ptr sense_ptr;
34730d9a4ef3SDoug Ambrisko 	uint32_t context = 0;
3474741367d5SDoug Ambrisko 	uint8_t *data = NULL, *temp;
3475741367d5SDoug Ambrisko 	int i;
34768ec5c98bSJohn Baldwin 	int error, locked;
3477741367d5SDoug Ambrisko 
3478741367d5SDoug Ambrisko 	sc = dev->si_drv1;
3479741367d5SDoug Ambrisko 	error = 0;
3480741367d5SDoug Ambrisko 	switch (cmd) {
3481c2be47f2SDoug Ambrisko 	case MFI_LINUX_CMD_2: /* Firmware Linux ioctl shim */
3482741367d5SDoug Ambrisko 		error = copyin(arg, &l_ioc, sizeof(l_ioc));
3483741367d5SDoug Ambrisko 		if (error != 0)
3484741367d5SDoug Ambrisko 			return (error);
3485741367d5SDoug Ambrisko 
3486741367d5SDoug Ambrisko 		if (l_ioc.lioc_sge_count > MAX_LINUX_IOCTL_SGE) {
3487741367d5SDoug Ambrisko 			return (EINVAL);
3488741367d5SDoug Ambrisko 		}
3489741367d5SDoug Ambrisko 
3490741367d5SDoug Ambrisko 		mtx_lock(&sc->mfi_io_lock);
3491741367d5SDoug Ambrisko 		if ((cm = mfi_dequeue_free(sc)) == NULL) {
3492741367d5SDoug Ambrisko 			mtx_unlock(&sc->mfi_io_lock);
3493741367d5SDoug Ambrisko 			return (EBUSY);
3494741367d5SDoug Ambrisko 		}
3495741367d5SDoug Ambrisko 		mtx_unlock(&sc->mfi_io_lock);
34968ec5c98bSJohn Baldwin 		locked = 0;
3497741367d5SDoug Ambrisko 
3498741367d5SDoug Ambrisko 		/*
3499741367d5SDoug Ambrisko 		 * save off original context since copying from user
3500741367d5SDoug Ambrisko 		 * will clobber some data
3501741367d5SDoug Ambrisko 		 */
3502741367d5SDoug Ambrisko 		context = cm->cm_frame->header.context;
3503741367d5SDoug Ambrisko 
3504741367d5SDoug Ambrisko 		bcopy(l_ioc.lioc_frame.raw, cm->cm_frame,
350504697de9SDoug Ambrisko 		      2 * MFI_DCMD_FRAME_SIZE);	/* this isn't quite right */
3506fa1e6ef4SDoug Ambrisko 		cm->cm_total_frame_size = (sizeof(union mfi_sgl)
3507fa1e6ef4SDoug Ambrisko 		      * l_ioc.lioc_sge_count) + l_ioc.lioc_sgl_off;
35080d9a4ef3SDoug Ambrisko 		cm->cm_frame->header.scsi_status = 0;
35090d9a4ef3SDoug Ambrisko 		cm->cm_frame->header.pad0 = 0;
351004697de9SDoug Ambrisko 		if (l_ioc.lioc_sge_count)
3511741367d5SDoug Ambrisko 			cm->cm_sg =
3512741367d5SDoug Ambrisko 			    (union mfi_sgl *)&cm->cm_frame->bytes[l_ioc.lioc_sgl_off];
351304697de9SDoug Ambrisko 		cm->cm_flags = 0;
351404697de9SDoug Ambrisko 		if (cm->cm_frame->header.flags & MFI_FRAME_DATAIN)
351504697de9SDoug Ambrisko 			cm->cm_flags |= MFI_CMD_DATAIN;
351604697de9SDoug Ambrisko 		if (cm->cm_frame->header.flags & MFI_FRAME_DATAOUT)
351704697de9SDoug Ambrisko 			cm->cm_flags |= MFI_CMD_DATAOUT;
3518741367d5SDoug Ambrisko 		cm->cm_len = cm->cm_frame->header.data_len;
3519fa1e6ef4SDoug Ambrisko 		if (cm->cm_len &&
3520fa1e6ef4SDoug Ambrisko 		      (cm->cm_flags & (MFI_CMD_DATAIN | MFI_CMD_DATAOUT))) {
3521741367d5SDoug Ambrisko 			cm->cm_data = data = malloc(cm->cm_len, M_MFIBUF,
3522741367d5SDoug Ambrisko 			    M_WAITOK | M_ZERO);
352304697de9SDoug Ambrisko 		} else {
352404697de9SDoug Ambrisko 			cm->cm_data = 0;
352504697de9SDoug Ambrisko 		}
3526741367d5SDoug Ambrisko 
3527741367d5SDoug Ambrisko 		/* restore header context */
3528741367d5SDoug Ambrisko 		cm->cm_frame->header.context = context;
3529741367d5SDoug Ambrisko 
3530741367d5SDoug Ambrisko 		temp = data;
353104697de9SDoug Ambrisko 		if (cm->cm_flags & MFI_CMD_DATAOUT) {
3532741367d5SDoug Ambrisko 			for (i = 0; i < l_ioc.lioc_sge_count; i++) {
35330929b669SScott Long 				error = copyin(PTRIN(l_ioc.lioc_sgl[i].iov_base),
3534741367d5SDoug Ambrisko 				       temp,
3535741367d5SDoug Ambrisko 				       l_ioc.lioc_sgl[i].iov_len);
3536741367d5SDoug Ambrisko 				if (error != 0) {
3537741367d5SDoug Ambrisko 					device_printf(sc->mfi_dev,
3538812819c7SDoug Ambrisko 					    "Copy in failed\n");
3539741367d5SDoug Ambrisko 					goto out;
3540741367d5SDoug Ambrisko 				}
3541741367d5SDoug Ambrisko 				temp = &temp[l_ioc.lioc_sgl[i].iov_len];
3542741367d5SDoug Ambrisko 			}
354304697de9SDoug Ambrisko 		}
3544741367d5SDoug Ambrisko 
35458ec5c98bSJohn Baldwin 		if (cm->cm_frame->header.cmd == MFI_CMD_DCMD)
35468ec5c98bSJohn Baldwin 			locked = mfi_config_lock(sc, cm->cm_frame->dcmd.opcode);
35478ec5c98bSJohn Baldwin 
35480a158415SDoug Ambrisko 		if (cm->cm_frame->header.cmd == MFI_CMD_PD_SCSI_IO) {
3549a6ba0fd6SDoug Ambrisko 			cm->cm_frame->pass.sense_addr_lo =
3550a6ba0fd6SDoug Ambrisko 			    (uint32_t)cm->cm_sense_busaddr;
3551a6ba0fd6SDoug Ambrisko 			cm->cm_frame->pass.sense_addr_hi =
3552a6ba0fd6SDoug Ambrisko 			    (uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32);
35530a158415SDoug Ambrisko 		}
35540a158415SDoug Ambrisko 
3555441f6d5dSScott Long 		mtx_lock(&sc->mfi_io_lock);
35568ec5c98bSJohn Baldwin 		error = mfi_check_command_pre(sc, cm);
35578ec5c98bSJohn Baldwin 		if (error) {
35588ec5c98bSJohn Baldwin 			mtx_unlock(&sc->mfi_io_lock);
35598ec5c98bSJohn Baldwin 			goto out;
35608ec5c98bSJohn Baldwin 		}
35618ec5c98bSJohn Baldwin 
35625be25877SDoug Ambrisko 		if ((error = mfi_wait_command(sc, cm)) != 0) {
3563741367d5SDoug Ambrisko 			device_printf(sc->mfi_dev,
3564812819c7SDoug Ambrisko 			    "Controller polled failed\n");
3565441f6d5dSScott Long 			mtx_unlock(&sc->mfi_io_lock);
3566741367d5SDoug Ambrisko 			goto out;
3567741367d5SDoug Ambrisko 		}
3568741367d5SDoug Ambrisko 
35698ec5c98bSJohn Baldwin 		mfi_check_command_post(sc, cm);
3570441f6d5dSScott Long 		mtx_unlock(&sc->mfi_io_lock);
3571741367d5SDoug Ambrisko 
3572741367d5SDoug Ambrisko 		temp = data;
357304697de9SDoug Ambrisko 		if (cm->cm_flags & MFI_CMD_DATAIN) {
3574741367d5SDoug Ambrisko 			for (i = 0; i < l_ioc.lioc_sge_count; i++) {
3575741367d5SDoug Ambrisko 				error = copyout(temp,
35760929b669SScott Long 					PTRIN(l_ioc.lioc_sgl[i].iov_base),
3577741367d5SDoug Ambrisko 					l_ioc.lioc_sgl[i].iov_len);
3578741367d5SDoug Ambrisko 				if (error != 0) {
3579741367d5SDoug Ambrisko 					device_printf(sc->mfi_dev,
3580812819c7SDoug Ambrisko 					    "Copy out failed\n");
3581741367d5SDoug Ambrisko 					goto out;
3582741367d5SDoug Ambrisko 				}
3583741367d5SDoug Ambrisko 				temp = &temp[l_ioc.lioc_sgl[i].iov_len];
3584741367d5SDoug Ambrisko 			}
358504697de9SDoug Ambrisko 		}
3586741367d5SDoug Ambrisko 
3587741367d5SDoug Ambrisko 		if (l_ioc.lioc_sense_len) {
3588fa1e6ef4SDoug Ambrisko 			/* get user-space sense ptr then copy out sense */
3589fa1e6ef4SDoug Ambrisko 			bcopy(&((struct mfi_linux_ioc_packet*)arg)
3590fa1e6ef4SDoug Ambrisko                             ->lioc_frame.raw[l_ioc.lioc_sense_off],
3591fa1e6ef4SDoug Ambrisko 			    &sense_ptr.sense_ptr_data[0],
3592fa1e6ef4SDoug Ambrisko 			    sizeof(sense_ptr.sense_ptr_data));
3593b89b182cSDoug Ambrisko #ifdef __amd64__
3594b89b182cSDoug Ambrisko 			/*
3595b89b182cSDoug Ambrisko 			 * only 32bit Linux support so zero out any
3596b89b182cSDoug Ambrisko 			 * address over 32bit
3597b89b182cSDoug Ambrisko 			 */
35984254ef59SDoug Ambrisko 			sense_ptr.addr.high = 0;
3599b89b182cSDoug Ambrisko #endif
3600fa1e6ef4SDoug Ambrisko 			error = copyout(cm->cm_sense, sense_ptr.user_space,
3601741367d5SDoug Ambrisko 			    l_ioc.lioc_sense_len);
3602741367d5SDoug Ambrisko 			if (error != 0) {
3603741367d5SDoug Ambrisko 				device_printf(sc->mfi_dev,
3604812819c7SDoug Ambrisko 				    "Copy out failed\n");
3605741367d5SDoug Ambrisko 				goto out;
3606741367d5SDoug Ambrisko 			}
3607741367d5SDoug Ambrisko 		}
3608741367d5SDoug Ambrisko 
3609741367d5SDoug Ambrisko 		error = copyout(&cm->cm_frame->header.cmd_status,
3610741367d5SDoug Ambrisko 			&((struct mfi_linux_ioc_packet*)arg)
3611741367d5SDoug Ambrisko 			->lioc_frame.hdr.cmd_status,
3612741367d5SDoug Ambrisko 			1);
3613741367d5SDoug Ambrisko 		if (error != 0) {
3614741367d5SDoug Ambrisko 			device_printf(sc->mfi_dev,
3615812819c7SDoug Ambrisko 				      "Copy out failed\n");
3616741367d5SDoug Ambrisko 			goto out;
3617741367d5SDoug Ambrisko 		}
3618741367d5SDoug Ambrisko 
3619741367d5SDoug Ambrisko out:
36208ec5c98bSJohn Baldwin 		mfi_config_unlock(sc, locked);
3621741367d5SDoug Ambrisko 		if (data)
3622741367d5SDoug Ambrisko 			free(data, M_MFIBUF);
3623741367d5SDoug Ambrisko 		if (cm) {
3624741367d5SDoug Ambrisko 			mtx_lock(&sc->mfi_io_lock);
3625741367d5SDoug Ambrisko 			mfi_release_command(cm);
3626741367d5SDoug Ambrisko 			mtx_unlock(&sc->mfi_io_lock);
3627741367d5SDoug Ambrisko 		}
3628741367d5SDoug Ambrisko 
3629741367d5SDoug Ambrisko 		return (error);
3630c2be47f2SDoug Ambrisko 	case MFI_LINUX_SET_AEN_2: /* AEN Linux ioctl shim */
3631741367d5SDoug Ambrisko 		error = copyin(arg, &l_aen, sizeof(l_aen));
3632741367d5SDoug Ambrisko 		if (error != 0)
3633741367d5SDoug Ambrisko 			return (error);
3634741367d5SDoug Ambrisko 		printf("AEN IMPLEMENTED for pid %d\n", curproc->p_pid);
3635741367d5SDoug Ambrisko 		mfi_aen_entry = malloc(sizeof(struct mfi_aen), M_MFIBUF,
3636741367d5SDoug Ambrisko 		    M_WAITOK);
3637441f6d5dSScott Long 		mtx_lock(&sc->mfi_io_lock);
3638741367d5SDoug Ambrisko 		mfi_aen_entry->p = curproc;
3639701308efSZhenlei Huang 		TAILQ_INSERT_TAIL(&sc->mfi_aen_pids, mfi_aen_entry, aen_link);
3640741367d5SDoug Ambrisko 		error = mfi_aen_register(sc, l_aen.laen_seq_num,
3641741367d5SDoug Ambrisko 		    l_aen.laen_class_locale);
3642741367d5SDoug Ambrisko 
3643741367d5SDoug Ambrisko 		if (error != 0) {
3644741367d5SDoug Ambrisko 			TAILQ_REMOVE(&sc->mfi_aen_pids, mfi_aen_entry,
3645741367d5SDoug Ambrisko 			    aen_link);
3646741367d5SDoug Ambrisko 			free(mfi_aen_entry, M_MFIBUF);
3647741367d5SDoug Ambrisko 		}
3648441f6d5dSScott Long 		mtx_unlock(&sc->mfi_io_lock);
3649741367d5SDoug Ambrisko 
3650741367d5SDoug Ambrisko 		return (error);
3651741367d5SDoug Ambrisko 	default:
3652741367d5SDoug Ambrisko 		device_printf(sc->mfi_dev, "IOCTL 0x%lx not handled\n", cmd);
3653741367d5SDoug Ambrisko 		error = ENOENT;
3654741367d5SDoug Ambrisko 		break;
3655741367d5SDoug Ambrisko 	}
3656741367d5SDoug Ambrisko 
3657741367d5SDoug Ambrisko 	return (error);
3658741367d5SDoug Ambrisko }
3659741367d5SDoug Ambrisko 
3660741367d5SDoug Ambrisko static int
mfi_poll(struct cdev * dev,int poll_events,struct thread * td)3661741367d5SDoug Ambrisko mfi_poll(struct cdev *dev, int poll_events, struct thread *td)
3662741367d5SDoug Ambrisko {
3663741367d5SDoug Ambrisko 	struct mfi_softc *sc;
3664741367d5SDoug Ambrisko 	int revents = 0;
3665741367d5SDoug Ambrisko 
3666741367d5SDoug Ambrisko 	sc = dev->si_drv1;
3667741367d5SDoug Ambrisko 
3668741367d5SDoug Ambrisko 	if (poll_events & (POLLIN | POLLRDNORM)) {
3669441f6d5dSScott Long 		if (sc->mfi_aen_triggered != 0) {
3670741367d5SDoug Ambrisko 			revents |= poll_events & (POLLIN | POLLRDNORM);
3671441f6d5dSScott Long 			sc->mfi_aen_triggered = 0;
3672441f6d5dSScott Long 		}
3673741367d5SDoug Ambrisko 		if (sc->mfi_aen_triggered == 0 && sc->mfi_aen_cm == NULL) {
3674741367d5SDoug Ambrisko 			revents |= POLLERR;
3675741367d5SDoug Ambrisko 		}
3676741367d5SDoug Ambrisko 	}
3677741367d5SDoug Ambrisko 
3678741367d5SDoug Ambrisko 	if (revents == 0) {
3679741367d5SDoug Ambrisko 		if (poll_events & (POLLIN | POLLRDNORM)) {
3680741367d5SDoug Ambrisko 			sc->mfi_poll_waiting = 1;
3681741367d5SDoug Ambrisko 			selrecord(td, &sc->mfi_select);
3682741367d5SDoug Ambrisko 		}
3683741367d5SDoug Ambrisko 	}
3684741367d5SDoug Ambrisko 
3685741367d5SDoug Ambrisko 	return revents;
3686741367d5SDoug Ambrisko }
36875ba21ff1SScott Long 
3688441f6d5dSScott Long static void
mfi_dump_all(void)3689441f6d5dSScott Long mfi_dump_all(void)
3690441f6d5dSScott Long {
3691441f6d5dSScott Long 	struct mfi_softc *sc;
3692441f6d5dSScott Long 	struct mfi_command *cm;
3693441f6d5dSScott Long 	devclass_t dc;
3694441f6d5dSScott Long 	time_t deadline;
36953dbe05f6SDimitry Andric 	int timedout __unused;
3696441f6d5dSScott Long 	int i;
3697441f6d5dSScott Long 
3698441f6d5dSScott Long 	dc = devclass_find("mfi");
3699441f6d5dSScott Long 	if (dc == NULL) {
3700441f6d5dSScott Long 		printf("No mfi dev class\n");
3701441f6d5dSScott Long 		return;
3702441f6d5dSScott Long 	}
3703441f6d5dSScott Long 
3704441f6d5dSScott Long 	for (i = 0; ; i++) {
3705441f6d5dSScott Long 		sc = devclass_get_softc(dc, i);
3706441f6d5dSScott Long 		if (sc == NULL)
3707441f6d5dSScott Long 			break;
3708441f6d5dSScott Long 		device_printf(sc->mfi_dev, "Dumping\n\n");
3709441f6d5dSScott Long 		timedout = 0;
3710f108bdaaSSteven Hartland 		deadline = time_uptime - mfi_cmd_timeout;
37115ba21ff1SScott Long 		mtx_lock(&sc->mfi_io_lock);
37125ba21ff1SScott Long 		TAILQ_FOREACH(cm, &sc->mfi_busy, cm_link) {
371308c89430SSteven Hartland 			if (cm->cm_timestamp <= deadline) {
37145ba21ff1SScott Long 				device_printf(sc->mfi_dev,
3715a6ba0fd6SDoug Ambrisko 				    "COMMAND %p TIMEOUT AFTER %d SECONDS\n",
3716a6ba0fd6SDoug Ambrisko 				    cm, (int)(time_uptime - cm->cm_timestamp));
37175ba21ff1SScott Long 				MFI_PRINT_CMD(cm);
37185ba21ff1SScott Long 				timedout++;
37195ba21ff1SScott Long 			}
37205ba21ff1SScott Long 		}
37215ba21ff1SScott Long 
37225ba21ff1SScott Long #if 0
37235ba21ff1SScott Long 		if (timedout)
372408c89430SSteven Hartland 			MFI_DUMP_CMDS(sc);
37255ba21ff1SScott Long #endif
37265ba21ff1SScott Long 
37275ba21ff1SScott Long 		mtx_unlock(&sc->mfi_io_lock);
3728441f6d5dSScott Long 	}
3729441f6d5dSScott Long 
3730441f6d5dSScott Long 	return;
3731441f6d5dSScott Long }
3732441f6d5dSScott Long 
3733441f6d5dSScott Long static void
mfi_timeout(void * data)3734441f6d5dSScott Long mfi_timeout(void *data)
3735441f6d5dSScott Long {
3736441f6d5dSScott Long 	struct mfi_softc *sc = (struct mfi_softc *)data;
373708c89430SSteven Hartland 	struct mfi_command *cm, *tmp;
3738441f6d5dSScott Long 	time_t deadline;
37393dbe05f6SDimitry Andric 	int timedout __unused = 0;
3740441f6d5dSScott Long 
3741f108bdaaSSteven Hartland 	deadline = time_uptime - mfi_cmd_timeout;
3742a6ba0fd6SDoug Ambrisko 	if (sc->adpreset == 0) {
3743a6ba0fd6SDoug Ambrisko 		if (!mfi_tbolt_reset(sc)) {
3744f108bdaaSSteven Hartland 			callout_reset(&sc->mfi_watchdog_callout,
3745f108bdaaSSteven Hartland 			    mfi_cmd_timeout * hz, mfi_timeout, sc);
37460d9a4ef3SDoug Ambrisko 			return;
37470d9a4ef3SDoug Ambrisko 		}
37480d9a4ef3SDoug Ambrisko 	}
3749441f6d5dSScott Long 	mtx_lock(&sc->mfi_io_lock);
375008c89430SSteven Hartland 	TAILQ_FOREACH_SAFE(cm, &sc->mfi_busy, cm_link, tmp) {
3751ddbffe7fSDoug Ambrisko 		if (sc->mfi_aen_cm == cm || sc->mfi_map_sync_cm == cm)
3752441f6d5dSScott Long 			continue;
375308c89430SSteven Hartland 		if (cm->cm_timestamp <= deadline) {
37540d9a4ef3SDoug Ambrisko 			if (sc->adpreset != 0 && sc->issuepend_done == 0) {
37550d9a4ef3SDoug Ambrisko 				cm->cm_timestamp = time_uptime;
3756a6ba0fd6SDoug Ambrisko 			} else {
3757441f6d5dSScott Long 				device_printf(sc->mfi_dev,
3758a6ba0fd6SDoug Ambrisko 				    "COMMAND %p TIMEOUT AFTER %d SECONDS\n",
3759a6ba0fd6SDoug Ambrisko 				     cm, (int)(time_uptime - cm->cm_timestamp)
3760a6ba0fd6SDoug Ambrisko 				     );
3761441f6d5dSScott Long 				MFI_PRINT_CMD(cm);
3762441f6d5dSScott Long 				MFI_VALIDATE_CMD(sc, cm);
376308c89430SSteven Hartland 				/*
37645dd4da9fSSteven Hartland 				 * While commands can get stuck forever we do
37655dd4da9fSSteven Hartland 				 * not fail them as there is no way to tell if
37665dd4da9fSSteven Hartland 				 * the controller has actually processed them
37675dd4da9fSSteven Hartland 				 * or not.
37685dd4da9fSSteven Hartland 				 *
37695dd4da9fSSteven Hartland 				 * In addition its very likely that force
37705dd4da9fSSteven Hartland 				 * failing a command here would cause a panic
37715dd4da9fSSteven Hartland 				 * e.g. in UFS.
377208c89430SSteven Hartland 				 */
3773441f6d5dSScott Long 				timedout++;
3774441f6d5dSScott Long 			}
3775441f6d5dSScott Long 		}
37760d9a4ef3SDoug Ambrisko 	}
3777441f6d5dSScott Long 
3778441f6d5dSScott Long #if 0
3779441f6d5dSScott Long 	if (timedout)
378008c89430SSteven Hartland 		MFI_DUMP_CMDS(sc);
3781441f6d5dSScott Long #endif
3782441f6d5dSScott Long 
3783441f6d5dSScott Long 	mtx_unlock(&sc->mfi_io_lock);
37845ba21ff1SScott Long 
3785f108bdaaSSteven Hartland 	callout_reset(&sc->mfi_watchdog_callout, mfi_cmd_timeout * hz,
37865ba21ff1SScott Long 	    mfi_timeout, sc);
37875ba21ff1SScott Long 
3788441f6d5dSScott Long 	if (0)
3789441f6d5dSScott Long 		mfi_dump_all();
37905ba21ff1SScott Long 	return;
37915ba21ff1SScott Long }
3792