159024Sobrien/*-
278828Sobrien * Copyright (c) 2006 IronPort Systems
3218822Sdim * All rights reserved.
477298Sobrien *
559024Sobrien * Redistribution and use in source and binary forms, with or without
659024Sobrien * modification, are permitted provided that the following conditions
759024Sobrien * are met:
859024Sobrien * 1. Redistributions of source code must retain the above copyright
959024Sobrien *    notice, this list of conditions and the following disclaimer.
1059024Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1159024Sobrien *    notice, this list of conditions and the following disclaimer in the
1259024Sobrien *    documentation and/or other materials provided with the distribution.
1359024Sobrien *
1459024Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1559024Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1659024Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1759024Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1859024Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19218822Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20218822Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2159024Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2259024Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2389857Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2459024Sobrien * SUCH DAMAGE.
2559024Sobrien */
2659024Sobrien/*-
27130561Sobrien * Copyright (c) 2007 LSI Corp.
2859024Sobrien * Copyright (c) 2007 Rajesh Prabhakaran.
2959024Sobrien * All rights reserved.
3059024Sobrien *
3177298Sobrien * Redistribution and use in source and binary forms, with or without
3259024Sobrien * modification, are permitted provided that the following conditions
3359024Sobrien * are met:
3491041Sobrien * 1. Redistributions of source code must retain the above copyright
3591041Sobrien *    notice, this list of conditions and the following disclaimer.
3691041Sobrien * 2. Redistributions in binary form must reproduce the above copyright
3791041Sobrien *    notice, this list of conditions and the following disclaimer in the
3891041Sobrien *    documentation and/or other materials provided with the distribution.
3991041Sobrien *
4059024Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
4159024Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4260484Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4359024Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
4459024Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
4559024Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
4659024Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4760484Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
4860484Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
4960484Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5059024Sobrien * SUCH DAMAGE.
5159024Sobrien */
5259024Sobrien
5359024Sobrien#include <sys/cdefs.h>
5459024Sobrien__FBSDID("$FreeBSD: stable/11/sys/dev/mfi/mfi.c 360534 2020-05-01 13:47:13Z mav $");
5559024Sobrien
5659024Sobrien#include "opt_compat.h"
5759024Sobrien#include "opt_mfi.h"
5859024Sobrien
5959024Sobrien#include <sys/param.h>
6059024Sobrien#include <sys/systm.h>
6159024Sobrien#include <sys/sysctl.h>
6259024Sobrien#include <sys/malloc.h>
6359024Sobrien#include <sys/kernel.h>
6459024Sobrien#include <sys/poll.h>
6559024Sobrien#include <sys/selinfo.h>
6659024Sobrien#include <sys/bus.h>
6759024Sobrien#include <sys/conf.h>
6859024Sobrien#include <sys/eventhandler.h>
6959024Sobrien#include <sys/rman.h>
7059024Sobrien#include <sys/bus_dma.h>
7159024Sobrien#include <sys/bio.h>
7259024Sobrien#include <sys/ioccom.h>
7359024Sobrien#include <sys/uio.h>
7459024Sobrien#include <sys/proc.h>
7559024Sobrien#include <sys/signalvar.h>
7659024Sobrien#include <sys/sysent.h>
7759024Sobrien#include <sys/taskqueue.h>
7859024Sobrien
7959024Sobrien#include <machine/bus.h>
8059024Sobrien#include <machine/resource.h>
8159024Sobrien
8259024Sobrien#include <dev/mfi/mfireg.h>
8359024Sobrien#include <dev/mfi/mfi_ioctl.h>
8459024Sobrien#include <dev/mfi/mfivar.h>
8559024Sobrien#include <sys/interrupt.h>
8659024Sobrien#include <sys/priority.h>
8759024Sobrien
8859024Sobrienstatic int	mfi_alloc_commands(struct mfi_softc *);
8959024Sobrienstatic int	mfi_comms_init(struct mfi_softc *);
9059024Sobrienstatic int	mfi_get_controller_info(struct mfi_softc *);
9159024Sobrienstatic int	mfi_get_log_state(struct mfi_softc *,
9259024Sobrien		    struct mfi_evt_log_state **);
9359024Sobrienstatic int	mfi_parse_entries(struct mfi_softc *, int, int);
9459024Sobrienstatic void	mfi_data_cb(void *, bus_dma_segment_t *, int, int);
9559024Sobrienstatic void	mfi_startup(void *arg);
9659024Sobrienstatic void	mfi_intr(void *arg);
9759024Sobrienstatic void	mfi_ldprobe(struct mfi_softc *sc);
9860484Sobrienstatic void	mfi_syspdprobe(struct mfi_softc *sc);
9960484Sobrienstatic void	mfi_handle_evt(void *context, int pending);
10060484Sobrienstatic int	mfi_aen_register(struct mfi_softc *sc, int seq, int locale);
10160484Sobrienstatic void	mfi_aen_complete(struct mfi_command *);
10277298Sobrienstatic int	mfi_add_ld(struct mfi_softc *sc, int);
10377298Sobrienstatic void	mfi_add_ld_complete(struct mfi_command *);
10477298Sobrienstatic int	mfi_add_sys_pd(struct mfi_softc *sc, int);
10559024Sobrienstatic void	mfi_add_sys_pd_complete(struct mfi_command *);
10659024Sobrienstatic struct mfi_command * mfi_bio_command(struct mfi_softc *);
10759024Sobrienstatic void	mfi_bio_complete(struct mfi_command *);
10859024Sobrienstatic struct mfi_command *mfi_build_ldio(struct mfi_softc *,struct bio*);
10959024Sobrienstatic struct mfi_command *mfi_build_syspdio(struct mfi_softc *,struct bio*);
11059024Sobrienstatic int	mfi_send_frame(struct mfi_softc *, struct mfi_command *);
11159024Sobrienstatic int	mfi_std_send_frame(struct mfi_softc *, struct mfi_command *);
11259024Sobrienstatic int	mfi_abort(struct mfi_softc *, struct mfi_command **);
11360484Sobrienstatic int	mfi_linux_ioctl_int(struct cdev *, u_long, caddr_t, int, struct thread *);
11460484Sobrienstatic void	mfi_timeout(void *);
11560484Sobrienstatic int	mfi_user_command(struct mfi_softc *,
11660484Sobrien		    struct mfi_ioc_passthru *);
11760484Sobrienstatic void	mfi_enable_intr_xscale(struct mfi_softc *sc);
118130561Sobrienstatic void	mfi_enable_intr_ppc(struct mfi_softc *sc);
119130561Sobrienstatic int32_t	mfi_read_fw_status_xscale(struct mfi_softc *sc);
120130561Sobrienstatic int32_t	mfi_read_fw_status_ppc(struct mfi_softc *sc);
12160484Sobrienstatic int	mfi_check_clear_intr_xscale(struct mfi_softc *sc);
12260484Sobrienstatic int	mfi_check_clear_intr_ppc(struct mfi_softc *sc);
12360484Sobrienstatic void 	mfi_issue_cmd_xscale(struct mfi_softc *sc, bus_addr_t bus_add,
12460484Sobrien		    uint32_t frame_cnt);
12560484Sobrienstatic void 	mfi_issue_cmd_ppc(struct mfi_softc *sc, bus_addr_t bus_add,
12659024Sobrien		    uint32_t frame_cnt);
12760484Sobrienstatic int mfi_config_lock(struct mfi_softc *sc, uint32_t opcode);
12859024Sobrienstatic void mfi_config_unlock(struct mfi_softc *sc, int locked);
12960484Sobrienstatic int mfi_check_command_pre(struct mfi_softc *sc, struct mfi_command *cm);
13059024Sobrienstatic void mfi_check_command_post(struct mfi_softc *sc, struct mfi_command *cm);
13177298Sobrienstatic int mfi_check_for_sscd(struct mfi_softc *sc, struct mfi_command *cm);
13259024Sobrien
13359024SobrienSYSCTL_NODE(_hw, OID_AUTO, mfi, CTLFLAG_RD, 0, "MFI driver parameters");
134218822Sdimstatic int	mfi_event_locale = MFI_EVT_LOCALE_ALL;
13559024SobrienSYSCTL_INT(_hw_mfi, OID_AUTO, event_locale, CTLFLAG_RWTUN, &mfi_event_locale,
13659024Sobrien           0, "event message locale");
13759024Sobrien
13859024Sobrienstatic int	mfi_event_class = MFI_EVT_CLASS_INFO;
13959024SobrienSYSCTL_INT(_hw_mfi, OID_AUTO, event_class, CTLFLAG_RWTUN, &mfi_event_class,
14059024Sobrien           0, "event message class");
14159024Sobrien
14260484Sobrienstatic int	mfi_max_cmds = 128;
143130561SobrienSYSCTL_INT(_hw_mfi, OID_AUTO, max_cmds, CTLFLAG_RDTUN, &mfi_max_cmds,
14460484Sobrien	   0, "Max commands limit (-1 = controller limit)");
145130561Sobrien
14659024Sobrienstatic int	mfi_detect_jbod_change = 1;
14759024SobrienSYSCTL_INT(_hw_mfi, OID_AUTO, detect_jbod_change, CTLFLAG_RWTUN,
14859024Sobrien	   &mfi_detect_jbod_change, 0, "Detect a change to a JBOD");
14977298Sobrien
15059024Sobrienint		mfi_polled_cmd_timeout = MFI_POLL_TIMEOUT_SECS;
15159024SobrienSYSCTL_INT(_hw_mfi, OID_AUTO, polled_cmd_timeout, CTLFLAG_RWTUN,
15259024Sobrien	   &mfi_polled_cmd_timeout, 0,
15359024Sobrien	   "Polled command timeout - used for firmware flash etc (in seconds)");
15460484Sobrien
15559024Sobrienstatic int	mfi_cmd_timeout = MFI_CMD_TIMEOUT;
15659024SobrienSYSCTL_INT(_hw_mfi, OID_AUTO, cmd_timeout, CTLFLAG_RWTUN, &mfi_cmd_timeout,
15759024Sobrien	   0, "Command timeout (in seconds)");
15859024Sobrien
15959024Sobrien/* Management interface */
16059024Sobrienstatic d_open_t		mfi_open;
16159024Sobrienstatic d_close_t	mfi_close;
16259024Sobrienstatic d_ioctl_t	mfi_ioctl;
16359024Sobrienstatic d_poll_t		mfi_poll;
16459024Sobrien
16559024Sobrienstatic struct cdevsw mfi_cdevsw = {
16677298Sobrien	.d_version = 	D_VERSION,
16759024Sobrien	.d_flags =	0,
16859024Sobrien	.d_open = 	mfi_open,
16959024Sobrien	.d_close =	mfi_close,
17060484Sobrien	.d_ioctl =	mfi_ioctl,
17159024Sobrien	.d_poll =	mfi_poll,
17259024Sobrien	.d_name =	"mfi",
17359024Sobrien};
17459024Sobrien
17559024SobrienMALLOC_DEFINE(M_MFIBUF, "mfibuf", "Buffers for the MFI driver");
17677298Sobrien
17777298Sobrien#define MFI_INQ_LENGTH SHORT_INQUIRY_LENGTH
17877298Sobrienstruct mfi_skinny_dma_info mfi_skinny;
17959024Sobrien
18059024Sobrienstatic void
18159024Sobrienmfi_enable_intr_xscale(struct mfi_softc *sc)
18277298Sobrien{
18359024Sobrien	MFI_WRITE4(sc, MFI_OMSK, 0x01);
18459024Sobrien}
18577298Sobrien
18659024Sobrienstatic void
18777298Sobrienmfi_enable_intr_ppc(struct mfi_softc *sc)
18859024Sobrien{
18959024Sobrien	if (sc->mfi_flags & MFI_FLAGS_1078) {
19077298Sobrien		MFI_WRITE4(sc, MFI_ODCR0, 0xFFFFFFFF);
19159024Sobrien		MFI_WRITE4(sc, MFI_OMSK, ~MFI_1078_EIM);
19277298Sobrien	}
19377298Sobrien	else if (sc->mfi_flags & MFI_FLAGS_GEN2) {
19459024Sobrien		MFI_WRITE4(sc, MFI_ODCR0, 0xFFFFFFFF);
19559024Sobrien		MFI_WRITE4(sc, MFI_OMSK, ~MFI_GEN2_EIM);
19677298Sobrien	}
19777298Sobrien	else if (sc->mfi_flags & MFI_FLAGS_SKINNY) {
19877298Sobrien		MFI_WRITE4(sc, MFI_OMSK, ~0x00000001);
19959024Sobrien	}
20059024Sobrien}
20159024Sobrien
20259024Sobrienstatic int32_t
20359024Sobrienmfi_read_fw_status_xscale(struct mfi_softc *sc)
20459024Sobrien{
205218822Sdim	return MFI_READ4(sc, MFI_OMSG0);
20659024Sobrien}
20759024Sobrien
20859024Sobrienstatic int32_t
20959024Sobrienmfi_read_fw_status_ppc(struct mfi_softc *sc)
21059024Sobrien{
21159024Sobrien	return MFI_READ4(sc, MFI_OSP0);
21259024Sobrien}
21360484Sobrien
21459024Sobrienstatic int
21559024Sobrienmfi_check_clear_intr_xscale(struct mfi_softc *sc)
21659024Sobrien{
21759024Sobrien	int32_t status;
21859024Sobrien
21959024Sobrien	status = MFI_READ4(sc, MFI_OSTS);
22059024Sobrien	if ((status & MFI_OSTS_INTR_VALID) == 0)
22159024Sobrien		return 1;
22259024Sobrien
22359024Sobrien	MFI_WRITE4(sc, MFI_OSTS, status);
22459024Sobrien	return 0;
22559024Sobrien}
22659024Sobrien
22759024Sobrienstatic int
22860484Sobrienmfi_check_clear_intr_ppc(struct mfi_softc *sc)
22977298Sobrien{
23060484Sobrien	int32_t status;
23159024Sobrien
23259024Sobrien	status = MFI_READ4(sc, MFI_OSTS);
23359024Sobrien	if (sc->mfi_flags & MFI_FLAGS_1078) {
23460484Sobrien		if (!(status & MFI_1078_RM)) {
23559024Sobrien			return 1;
23659024Sobrien		}
23759024Sobrien	}
23859024Sobrien	else if (sc->mfi_flags & MFI_FLAGS_GEN2) {
23959024Sobrien		if (!(status & MFI_GEN2_RM)) {
24059024Sobrien			return 1;
24160484Sobrien		}
24260484Sobrien	}
24360484Sobrien	else if (sc->mfi_flags & MFI_FLAGS_SKINNY) {
24460484Sobrien		if (!(status & MFI_SKINNY_RM)) {
24560484Sobrien			return 1;
24660484Sobrien		}
24760484Sobrien	}
24860484Sobrien	if (sc->mfi_flags & MFI_FLAGS_SKINNY)
24977298Sobrien		MFI_WRITE4(sc, MFI_OSTS, status);
25060484Sobrien	else
25160484Sobrien		MFI_WRITE4(sc, MFI_ODCR0, status);
25277298Sobrien	return 0;
25359024Sobrien}
25459024Sobrien
25560484Sobrienstatic void
25660484Sobrienmfi_issue_cmd_xscale(struct mfi_softc *sc, bus_addr_t bus_add, uint32_t frame_cnt)
25759024Sobrien{
25859024Sobrien	MFI_WRITE4(sc, MFI_IQP,(bus_add >>3)|frame_cnt);
25960484Sobrien}
26060484Sobrien
26160484Sobrienstatic void
26259024Sobrienmfi_issue_cmd_ppc(struct mfi_softc *sc, bus_addr_t bus_add, uint32_t frame_cnt)
26359024Sobrien{
26459024Sobrien	if (sc->mfi_flags & MFI_FLAGS_SKINNY) {
26559024Sobrien	    MFI_WRITE4(sc, MFI_IQPL, (bus_add | frame_cnt <<1)|1 );
26659024Sobrien	    MFI_WRITE4(sc, MFI_IQPH, 0x00000000);
26759024Sobrien	} else {
26859024Sobrien	    MFI_WRITE4(sc, MFI_IQP, (bus_add | frame_cnt <<1)|1 );
26959024Sobrien	}
27059024Sobrien}
27159024Sobrien
27259024Sobrienint
27359024Sobrienmfi_transition_firmware(struct mfi_softc *sc)
27459024Sobrien{
27559024Sobrien	uint32_t fw_state, cur_state;
27659024Sobrien	int max_wait, i;
27759024Sobrien	uint32_t cur_abs_reg_val = 0;
27859024Sobrien	uint32_t prev_abs_reg_val = 0;
27959024Sobrien
28059024Sobrien	cur_abs_reg_val = sc->mfi_read_fw_status(sc);
28159024Sobrien	fw_state = cur_abs_reg_val & MFI_FWSTATE_MASK;
28259024Sobrien	while (fw_state != MFI_FWSTATE_READY) {
28359024Sobrien		if (bootverbose)
28459024Sobrien			device_printf(sc->mfi_dev, "Waiting for firmware to "
28559024Sobrien			"become ready\n");
28660484Sobrien		cur_state = fw_state;
28759024Sobrien		switch (fw_state) {
28859024Sobrien		case MFI_FWSTATE_FAULT:
28959024Sobrien			device_printf(sc->mfi_dev, "Firmware fault\n");
29060484Sobrien			return (ENXIO);
29159024Sobrien		case MFI_FWSTATE_WAIT_HANDSHAKE:
29259024Sobrien			if (sc->mfi_flags & MFI_FLAGS_SKINNY || sc->mfi_flags & MFI_FLAGS_TBOLT)
29360484Sobrien			    MFI_WRITE4(sc, MFI_SKINNY_IDB, MFI_FWINIT_CLEAR_HANDSHAKE);
29459024Sobrien			else
29559024Sobrien			    MFI_WRITE4(sc, MFI_IDB, MFI_FWINIT_CLEAR_HANDSHAKE);
29659024Sobrien			max_wait = MFI_RESET_WAIT_TIME;
29759024Sobrien			break;
29859024Sobrien		case MFI_FWSTATE_OPERATIONAL:
29959024Sobrien			if (sc->mfi_flags & MFI_FLAGS_SKINNY || sc->mfi_flags & MFI_FLAGS_TBOLT)
30059024Sobrien			    MFI_WRITE4(sc, MFI_SKINNY_IDB, 7);
30159024Sobrien			else
30259024Sobrien			    MFI_WRITE4(sc, MFI_IDB, MFI_FWINIT_READY);
30359024Sobrien			max_wait = MFI_RESET_WAIT_TIME;
30459024Sobrien			break;
30559024Sobrien		case MFI_FWSTATE_UNDEFINED:
30659024Sobrien		case MFI_FWSTATE_BB_INIT:
30759024Sobrien			max_wait = MFI_RESET_WAIT_TIME;
30859024Sobrien			break;
30959024Sobrien		case MFI_FWSTATE_FW_INIT_2:
31059024Sobrien			max_wait = MFI_RESET_WAIT_TIME;
31160484Sobrien			break;
31260484Sobrien		case MFI_FWSTATE_FW_INIT:
31360484Sobrien		case MFI_FWSTATE_FLUSH_CACHE:
31460484Sobrien			max_wait = MFI_RESET_WAIT_TIME;
31577298Sobrien			break;
31677298Sobrien		case MFI_FWSTATE_DEVICE_SCAN:
31759024Sobrien			max_wait = MFI_RESET_WAIT_TIME; /* wait for 180 seconds */
31859024Sobrien			prev_abs_reg_val = cur_abs_reg_val;
31959024Sobrien			break;
32059024Sobrien		case MFI_FWSTATE_BOOT_MESSAGE_PENDING:
32159024Sobrien			if (sc->mfi_flags & MFI_FLAGS_SKINNY || sc->mfi_flags & MFI_FLAGS_TBOLT)
32259024Sobrien			    MFI_WRITE4(sc, MFI_SKINNY_IDB, MFI_FWINIT_HOTPLUG);
32359024Sobrien			else
32459024Sobrien			    MFI_WRITE4(sc, MFI_IDB, MFI_FWINIT_HOTPLUG);
32559024Sobrien			max_wait = MFI_RESET_WAIT_TIME;
32659024Sobrien			break;
32759024Sobrien		default:
32859024Sobrien			device_printf(sc->mfi_dev, "Unknown firmware state %#x\n",
32959024Sobrien			    fw_state);
33059024Sobrien			return (ENXIO);
33159024Sobrien		}
33259024Sobrien		for (i = 0; i < (max_wait * 10); i++) {
33359024Sobrien			cur_abs_reg_val = sc->mfi_read_fw_status(sc);
33459024Sobrien			fw_state = cur_abs_reg_val & MFI_FWSTATE_MASK;
335218822Sdim			if (fw_state == cur_state)
336218822Sdim				DELAY(100000);
337218822Sdim			else
338218822Sdim				break;
33959024Sobrien		}
340218822Sdim		if (fw_state == MFI_FWSTATE_DEVICE_SCAN) {
34159024Sobrien			/* Check the device scanning progress */
34259024Sobrien			if (prev_abs_reg_val != cur_abs_reg_val) {
34359024Sobrien				continue;
34459024Sobrien			}
34559024Sobrien		}
34677298Sobrien		if (fw_state == cur_state) {
34759024Sobrien			device_printf(sc->mfi_dev, "Firmware stuck in state "
34859024Sobrien			    "%#x\n", fw_state);
34959024Sobrien			return (ENXIO);
35059024Sobrien		}
35159024Sobrien	}
35259024Sobrien	return (0);
35359024Sobrien}
35459024Sobrien
35577298Sobrienstatic void
35659024Sobrienmfi_addr_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
35759024Sobrien{
35859024Sobrien	bus_addr_t *addr;
35959024Sobrien
36059024Sobrien	addr = arg;
36177298Sobrien	*addr = segs[0].ds_addr;
36277298Sobrien}
36377298Sobrien
36477298Sobrien
36577298Sobrienint
36677298Sobrienmfi_attach(struct mfi_softc *sc)
36777298Sobrien{
36877298Sobrien	uint32_t status;
36959024Sobrien	int error, commsz, framessz, sensesz;
37059024Sobrien	int frames, unit, max_fw_sge, max_fw_cmds;
37159024Sobrien	uint32_t tb_mem_size = 0;
37259024Sobrien	struct cdev *dev_t;
37359024Sobrien
37459024Sobrien	if (sc == NULL)
37559024Sobrien		return EINVAL;
37659024Sobrien
37777298Sobrien	device_printf(sc->mfi_dev, "Megaraid SAS driver Ver %s \n",
37859024Sobrien	    MEGASAS_VERSION);
37959024Sobrien
38059024Sobrien	mtx_init(&sc->mfi_io_lock, "MFI I/O lock", NULL, MTX_DEF);
38159024Sobrien	sx_init(&sc->mfi_config_lock, "MFI config");
38259024Sobrien	TAILQ_INIT(&sc->mfi_ld_tqh);
38359024Sobrien	TAILQ_INIT(&sc->mfi_syspd_tqh);
38459024Sobrien	TAILQ_INIT(&sc->mfi_ld_pend_tqh);
38559024Sobrien	TAILQ_INIT(&sc->mfi_syspd_pend_tqh);
38659024Sobrien	TAILQ_INIT(&sc->mfi_evt_queue);
38759024Sobrien	TASK_INIT(&sc->mfi_evt_task, 0, mfi_handle_evt, sc);
38859024Sobrien	TASK_INIT(&sc->mfi_map_sync_task, 0, mfi_handle_map_sync, sc);
38959024Sobrien	TAILQ_INIT(&sc->mfi_aen_pids);
39059024Sobrien	TAILQ_INIT(&sc->mfi_cam_ccbq);
39159024Sobrien
39259024Sobrien	mfi_initq_free(sc);
39359024Sobrien	mfi_initq_ready(sc);
39459024Sobrien	mfi_initq_busy(sc);
39559024Sobrien	mfi_initq_bio(sc);
39659024Sobrien
39759024Sobrien	sc->adpreset = 0;
39859024Sobrien	sc->last_seq_num = 0;
39959024Sobrien	sc->disableOnlineCtrlReset = 1;
40059024Sobrien	sc->issuepend_done = 1;
401104834Sobrien	sc->hw_crit_error = 0;
40259024Sobrien
40359024Sobrien	if (sc->mfi_flags & MFI_FLAGS_1064R) {
404104834Sobrien		sc->mfi_enable_intr = mfi_enable_intr_xscale;
40559024Sobrien		sc->mfi_read_fw_status = mfi_read_fw_status_xscale;
406104834Sobrien		sc->mfi_check_clear_intr = mfi_check_clear_intr_xscale;
40759024Sobrien		sc->mfi_issue_cmd = mfi_issue_cmd_xscale;
40859024Sobrien	} else if (sc->mfi_flags & MFI_FLAGS_TBOLT) {
40959024Sobrien		sc->mfi_enable_intr = mfi_tbolt_enable_intr_ppc;
41059024Sobrien		sc->mfi_disable_intr = mfi_tbolt_disable_intr_ppc;
41159024Sobrien		sc->mfi_read_fw_status = mfi_tbolt_read_fw_status_ppc;
41259024Sobrien		sc->mfi_check_clear_intr = mfi_tbolt_check_clear_intr_ppc;
41359024Sobrien		sc->mfi_issue_cmd = mfi_tbolt_issue_cmd_ppc;
41459024Sobrien		sc->mfi_adp_reset = mfi_tbolt_adp_reset;
41559024Sobrien		sc->mfi_tbolt = 1;
41659024Sobrien		TAILQ_INIT(&sc->mfi_cmd_tbolt_tqh);
41759024Sobrien	} else {
41859024Sobrien		sc->mfi_enable_intr =  mfi_enable_intr_ppc;
41959024Sobrien		sc->mfi_read_fw_status = mfi_read_fw_status_ppc;
42059024Sobrien		sc->mfi_check_clear_intr = mfi_check_clear_intr_ppc;
42159024Sobrien		sc->mfi_issue_cmd = mfi_issue_cmd_ppc;
42259024Sobrien	}
42359024Sobrien
42459024Sobrien
42559024Sobrien	/* Before we get too far, see if the firmware is working */
42659024Sobrien	if ((error = mfi_transition_firmware(sc)) != 0) {
42759024Sobrien		device_printf(sc->mfi_dev, "Firmware not in READY state, "
42859024Sobrien		    "error %d\n", error);
42959024Sobrien		return (ENXIO);
43059024Sobrien	}
43159024Sobrien
43259024Sobrien	/* Start: LSIP200113393 */
43359024Sobrien	if (bus_dma_tag_create( sc->mfi_parent_dmat,	/* parent */
43459024Sobrien				1, 0,			/* algnmnt, boundary */
43559024Sobrien				BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
43660484Sobrien				BUS_SPACE_MAXADDR,	/* highaddr */
43760484Sobrien				NULL, NULL,		/* filter, filterarg */
43860484Sobrien				MEGASAS_MAX_NAME*sizeof(bus_addr_t),			/* maxsize */
43960484Sobrien				1,			/* msegments */
44060484Sobrien				MEGASAS_MAX_NAME*sizeof(bus_addr_t),			/* maxsegsize */
44160484Sobrien				0,			/* flags */
44260484Sobrien				NULL, NULL,		/* lockfunc, lockarg */
44360484Sobrien				&sc->verbuf_h_dmat)) {
44477298Sobrien		device_printf(sc->mfi_dev, "Cannot allocate verbuf_h_dmat DMA tag\n");
44577298Sobrien		return (ENOMEM);
44677298Sobrien	}
44777298Sobrien	if (bus_dmamem_alloc(sc->verbuf_h_dmat, (void **)&sc->verbuf,
44859024Sobrien	    BUS_DMA_NOWAIT, &sc->verbuf_h_dmamap)) {
44959024Sobrien		device_printf(sc->mfi_dev, "Cannot allocate verbuf_h_dmamap memory\n");
45059024Sobrien		return (ENOMEM);
45177298Sobrien	}
45277298Sobrien	bzero(sc->verbuf, MEGASAS_MAX_NAME*sizeof(bus_addr_t));
45359024Sobrien	bus_dmamap_load(sc->verbuf_h_dmat, sc->verbuf_h_dmamap,
45459024Sobrien	    sc->verbuf, MEGASAS_MAX_NAME*sizeof(bus_addr_t),
45559024Sobrien	    mfi_addr_cb, &sc->verbuf_h_busaddr, 0);
45659024Sobrien	/* End: LSIP200113393 */
45759024Sobrien
45859024Sobrien	/*
45959024Sobrien	 * Get information needed for sizing the contiguous memory for the
46059024Sobrien	 * frame pool.  Size down the sgl parameter since we know that
46159024Sobrien	 * we will never need more than what's required for MAXPHYS.
46259024Sobrien	 * It would be nice if these constants were available at runtime
46359024Sobrien	 * instead of compile time.
46459024Sobrien	 */
46559024Sobrien	status = sc->mfi_read_fw_status(sc);
46659024Sobrien	max_fw_cmds = status & MFI_FWSTATE_MAXCMD_MASK;
46759024Sobrien	if (mfi_max_cmds > 0 && mfi_max_cmds < max_fw_cmds) {
46859024Sobrien		device_printf(sc->mfi_dev, "FW MaxCmds = %d, limiting to %d\n",
46959024Sobrien		    max_fw_cmds, mfi_max_cmds);
47059024Sobrien		sc->mfi_max_fw_cmds = mfi_max_cmds;
47177298Sobrien	} else {
47277298Sobrien		sc->mfi_max_fw_cmds = max_fw_cmds;
47377298Sobrien	}
47477298Sobrien	max_fw_sge = (status & MFI_FWSTATE_MAXSGL_MASK) >> 16;
47577298Sobrien	sc->mfi_max_sge = min(max_fw_sge, ((MFI_MAXPHYS / PAGE_SIZE) + 1));
47677298Sobrien
47777298Sobrien	/* ThunderBolt Support get the contiguous memory */
47859024Sobrien
47959024Sobrien	if (sc->mfi_flags & MFI_FLAGS_TBOLT) {
48059024Sobrien		mfi_tbolt_init_globals(sc);
48159024Sobrien		device_printf(sc->mfi_dev, "MaxCmd = %d, Drv MaxCmd = %d, "
48259024Sobrien		    "MaxSgl = %d, state = %#x\n", max_fw_cmds,
48359024Sobrien		    sc->mfi_max_fw_cmds, sc->mfi_max_sge, status);
48459024Sobrien		tb_mem_size = mfi_tbolt_get_memory_requirement(sc);
48559024Sobrien
48659024Sobrien		if (bus_dma_tag_create( sc->mfi_parent_dmat,	/* parent */
48759024Sobrien				1, 0,			/* algnmnt, boundary */
48877298Sobrien				BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
48977298Sobrien				BUS_SPACE_MAXADDR,	/* highaddr */
49077298Sobrien				NULL, NULL,		/* filter, filterarg */
49177298Sobrien				tb_mem_size,		/* maxsize */
49259024Sobrien				1,			/* msegments */
49359024Sobrien				tb_mem_size,		/* maxsegsize */
49459024Sobrien				0,			/* flags */
49559024Sobrien				NULL, NULL,		/* lockfunc, lockarg */
49659024Sobrien				&sc->mfi_tb_dmat)) {
49760484Sobrien			device_printf(sc->mfi_dev, "Cannot allocate comms DMA tag\n");
49859024Sobrien			return (ENOMEM);
49959024Sobrien		}
50059024Sobrien		if (bus_dmamem_alloc(sc->mfi_tb_dmat, (void **)&sc->request_message_pool,
50159024Sobrien		BUS_DMA_NOWAIT, &sc->mfi_tb_dmamap)) {
50259024Sobrien			device_printf(sc->mfi_dev, "Cannot allocate comms memory\n");
50359024Sobrien			return (ENOMEM);
50459024Sobrien		}
50559024Sobrien		bzero(sc->request_message_pool, tb_mem_size);
50659024Sobrien		bus_dmamap_load(sc->mfi_tb_dmat, sc->mfi_tb_dmamap,
50759024Sobrien		sc->request_message_pool, tb_mem_size, mfi_addr_cb, &sc->mfi_tb_busaddr, 0);
50859024Sobrien
50959024Sobrien		/* For ThunderBolt memory init */
51059024Sobrien		if (bus_dma_tag_create( sc->mfi_parent_dmat,	/* parent */
51159024Sobrien				0x100, 0,		/* alignmnt, boundary */
51259024Sobrien				BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
51359024Sobrien				BUS_SPACE_MAXADDR,	/* highaddr */
51459024Sobrien				NULL, NULL,		/* filter, filterarg */
51560484Sobrien				MFI_FRAME_SIZE,		/* maxsize */
51660484Sobrien				1,			/* msegments */
51759024Sobrien				MFI_FRAME_SIZE,		/* maxsegsize */
51860484Sobrien				0,			/* flags */
51960484Sobrien				NULL, NULL,		/* lockfunc, lockarg */
52060484Sobrien				&sc->mfi_tb_init_dmat)) {
52160484Sobrien			device_printf(sc->mfi_dev, "Cannot allocate init DMA tag\n");
52260484Sobrien			return (ENOMEM);
52360484Sobrien		}
52460484Sobrien		if (bus_dmamem_alloc(sc->mfi_tb_init_dmat, (void **)&sc->mfi_tb_init,
52559024Sobrien		    BUS_DMA_NOWAIT, &sc->mfi_tb_init_dmamap)) {
52659024Sobrien			device_printf(sc->mfi_dev, "Cannot allocate init memory\n");
52759024Sobrien			return (ENOMEM);
52859024Sobrien		}
52959024Sobrien		bzero(sc->mfi_tb_init, MFI_FRAME_SIZE);
53059024Sobrien		bus_dmamap_load(sc->mfi_tb_init_dmat, sc->mfi_tb_init_dmamap,
53159024Sobrien		sc->mfi_tb_init, MFI_FRAME_SIZE, mfi_addr_cb,
53259024Sobrien		    &sc->mfi_tb_init_busaddr, 0);
53359024Sobrien		if (mfi_tbolt_init_desc_pool(sc, sc->request_message_pool,
53459024Sobrien		    tb_mem_size)) {
53559024Sobrien			device_printf(sc->mfi_dev,
53659024Sobrien			    "Thunderbolt pool preparation error\n");
53759024Sobrien			return 0;
53859024Sobrien		}
53959024Sobrien
54059024Sobrien		/*
54159024Sobrien		  Allocate DMA memory mapping for MPI2 IOC Init descriptor,
54259024Sobrien		  we are taking it different from what we have allocated for Request
54359024Sobrien		  and reply descriptors to avoid confusion later
54459024Sobrien		*/
54559024Sobrien		tb_mem_size = sizeof(struct MPI2_IOC_INIT_REQUEST);
54659024Sobrien		if (bus_dma_tag_create( sc->mfi_parent_dmat,	/* parent */
54759024Sobrien				1, 0,			/* algnmnt, boundary */
548218822Sdim				BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
54959024Sobrien				BUS_SPACE_MAXADDR,	/* highaddr */
55059024Sobrien				NULL, NULL,		/* filter, filterarg */
55159024Sobrien				tb_mem_size,		/* maxsize */
55259024Sobrien				1,			/* msegments */
553218822Sdim				tb_mem_size,		/* maxsegsize */
55459024Sobrien				0,			/* flags */
55559024Sobrien				NULL, NULL,		/* lockfunc, lockarg */
55659024Sobrien				&sc->mfi_tb_ioc_init_dmat)) {
55759024Sobrien			device_printf(sc->mfi_dev,
55860484Sobrien			    "Cannot allocate comms DMA tag\n");
55959024Sobrien			return (ENOMEM);
56059024Sobrien		}
56159024Sobrien		if (bus_dmamem_alloc(sc->mfi_tb_ioc_init_dmat,
56259024Sobrien		    (void **)&sc->mfi_tb_ioc_init_desc,
56359024Sobrien		    BUS_DMA_NOWAIT, &sc->mfi_tb_ioc_init_dmamap)) {
56459024Sobrien			device_printf(sc->mfi_dev, "Cannot allocate comms memory\n");
56559024Sobrien			return (ENOMEM);
56659024Sobrien		}
56759024Sobrien		bzero(sc->mfi_tb_ioc_init_desc, tb_mem_size);
56859024Sobrien		bus_dmamap_load(sc->mfi_tb_ioc_init_dmat, sc->mfi_tb_ioc_init_dmamap,
56959024Sobrien		sc->mfi_tb_ioc_init_desc, tb_mem_size, mfi_addr_cb,
57059024Sobrien		    &sc->mfi_tb_ioc_init_busaddr, 0);
57159024Sobrien	}
57259024Sobrien	/*
57359024Sobrien	 * Create the dma tag for data buffers.  Used both for block I/O
57459024Sobrien	 * and for various internal data queries.
57559024Sobrien	 */
57659024Sobrien	if (bus_dma_tag_create( sc->mfi_parent_dmat,	/* parent */
57759024Sobrien				1, 0,			/* algnmnt, boundary */
57859024Sobrien				BUS_SPACE_MAXADDR,	/* lowaddr */
57959024Sobrien				BUS_SPACE_MAXADDR,	/* highaddr */
58059024Sobrien				NULL, NULL,		/* filter, filterarg */
58159024Sobrien				BUS_SPACE_MAXSIZE_32BIT,/* maxsize */
58277298Sobrien				sc->mfi_max_sge,	/* nsegments */
58359024Sobrien				BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
58459024Sobrien				BUS_DMA_ALLOCNOW,	/* flags */
58559024Sobrien				busdma_lock_mutex,	/* lockfunc */
58677298Sobrien				&sc->mfi_io_lock,	/* lockfuncarg */
58759024Sobrien				&sc->mfi_buffer_dmat)) {
58859024Sobrien		device_printf(sc->mfi_dev, "Cannot allocate buffer DMA tag\n");
58959024Sobrien		return (ENOMEM);
59077298Sobrien	}
59159024Sobrien
59259024Sobrien	/*
59359024Sobrien	 * Allocate DMA memory for the comms queues.  Keep it under 4GB for
59459024Sobrien	 * efficiency.  The mfi_hwcomms struct includes space for 1 reply queue
59560484Sobrien	 * entry, so the calculated size here will be will be 1 more than
59659024Sobrien	 * mfi_max_fw_cmds.  This is apparently a requirement of the hardware.
59759024Sobrien	 */
59859024Sobrien	commsz = (sizeof(uint32_t) * sc->mfi_max_fw_cmds) +
59960484Sobrien	    sizeof(struct mfi_hwcomms);
60060484Sobrien	if (bus_dma_tag_create( sc->mfi_parent_dmat,	/* parent */
60160484Sobrien				1, 0,			/* algnmnt, boundary */
60260484Sobrien				BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
60360484Sobrien				BUS_SPACE_MAXADDR,	/* highaddr */
60460484Sobrien				NULL, NULL,		/* filter, filterarg */
60560484Sobrien				commsz,			/* maxsize */
60660484Sobrien				1,			/* msegments */
60759024Sobrien				commsz,			/* maxsegsize */
60859024Sobrien				0,			/* flags */
60977298Sobrien				NULL, NULL,		/* lockfunc, lockarg */
61077298Sobrien				&sc->mfi_comms_dmat)) {
61177298Sobrien		device_printf(sc->mfi_dev, "Cannot allocate comms DMA tag\n");
61277298Sobrien		return (ENOMEM);
61377298Sobrien	}
61477298Sobrien	if (bus_dmamem_alloc(sc->mfi_comms_dmat, (void **)&sc->mfi_comms,
61577298Sobrien	    BUS_DMA_NOWAIT, &sc->mfi_comms_dmamap)) {
61677298Sobrien		device_printf(sc->mfi_dev, "Cannot allocate comms memory\n");
61759024Sobrien		return (ENOMEM);
61859024Sobrien	}
61959024Sobrien	bzero(sc->mfi_comms, commsz);
62059024Sobrien	bus_dmamap_load(sc->mfi_comms_dmat, sc->mfi_comms_dmamap,
62159024Sobrien	    sc->mfi_comms, commsz, mfi_addr_cb, &sc->mfi_comms_busaddr, 0);
62259024Sobrien	/*
62359024Sobrien	 * Allocate DMA memory for the command frames.  Keep them in the
62459024Sobrien	 * lower 4GB for efficiency.  Calculate the size of the commands at
62559024Sobrien	 * the same time; each command is one 64 byte frame plus a set of
62659024Sobrien         * additional frames for holding sg lists or other data.
62759024Sobrien	 * The assumption here is that the SG list will start at the second
62859024Sobrien	 * frame and not use the unused bytes in the first frame.  While this
62977298Sobrien	 * isn't technically correct, it simplifies the calculation and allows
63059024Sobrien	 * for command frames that might be larger than an mfi_io_frame.
63159024Sobrien	 */
63259024Sobrien	if (sizeof(bus_addr_t) == 8) {
63359024Sobrien		sc->mfi_sge_size = sizeof(struct mfi_sg64);
63459024Sobrien		sc->mfi_flags |= MFI_FLAGS_SG64;
63559024Sobrien	} else {
63677298Sobrien		sc->mfi_sge_size = sizeof(struct mfi_sg32);
63777298Sobrien	}
63859024Sobrien	if (sc->mfi_flags & MFI_FLAGS_SKINNY)
63959024Sobrien		sc->mfi_sge_size = sizeof(struct mfi_sg_skinny);
64077298Sobrien	frames = (sc->mfi_sge_size * sc->mfi_max_sge - 1) / MFI_FRAME_SIZE + 2;
64177298Sobrien	sc->mfi_cmd_size = frames * MFI_FRAME_SIZE;
64259024Sobrien	framessz = sc->mfi_cmd_size * sc->mfi_max_fw_cmds;
64359024Sobrien	if (bus_dma_tag_create( sc->mfi_parent_dmat,	/* parent */
64489857Sobrien				64, 0,			/* algnmnt, boundary */
64577298Sobrien				BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
64677298Sobrien				BUS_SPACE_MAXADDR,	/* highaddr */
64777298Sobrien				NULL, NULL,		/* filter, filterarg */
64877298Sobrien				framessz,		/* maxsize */
64989857Sobrien				1,			/* nsegments */
65077298Sobrien				framessz,		/* maxsegsize */
65159024Sobrien				0,			/* flags */
65277298Sobrien				NULL, NULL,		/* lockfunc, lockarg */
65377298Sobrien				&sc->mfi_frames_dmat)) {
65477298Sobrien		device_printf(sc->mfi_dev, "Cannot allocate frame DMA tag\n");
65577298Sobrien		return (ENOMEM);
65677298Sobrien	}
65789857Sobrien	if (bus_dmamem_alloc(sc->mfi_frames_dmat, (void **)&sc->mfi_frames,
65877298Sobrien	    BUS_DMA_NOWAIT, &sc->mfi_frames_dmamap)) {
65977298Sobrien		device_printf(sc->mfi_dev, "Cannot allocate frames memory\n");
66077298Sobrien		return (ENOMEM);
66177298Sobrien	}
66289857Sobrien	bzero(sc->mfi_frames, framessz);
66377298Sobrien	bus_dmamap_load(sc->mfi_frames_dmat, sc->mfi_frames_dmamap,
66477298Sobrien	    sc->mfi_frames, framessz, mfi_addr_cb, &sc->mfi_frames_busaddr,0);
66577298Sobrien	/*
66659024Sobrien	 * Allocate DMA memory for the frame sense data.  Keep them in the
66759024Sobrien	 * lower 4GB for efficiency
66859024Sobrien	 */
66977298Sobrien	sensesz = sc->mfi_max_fw_cmds * MFI_SENSE_LEN;
67077298Sobrien	if (bus_dma_tag_create( sc->mfi_parent_dmat,	/* parent */
67177298Sobrien				4, 0,			/* algnmnt, boundary */
67259024Sobrien				BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
67360484Sobrien				BUS_SPACE_MAXADDR,	/* highaddr */
67460484Sobrien				NULL, NULL,		/* filter, filterarg */
67559024Sobrien				sensesz,		/* maxsize */
67659024Sobrien				1,			/* nsegments */
67760484Sobrien				sensesz,		/* maxsegsize */
67859024Sobrien				0,			/* flags */
67960484Sobrien				NULL, NULL,		/* lockfunc, lockarg */
68060484Sobrien				&sc->mfi_sense_dmat)) {
68160484Sobrien		device_printf(sc->mfi_dev, "Cannot allocate sense DMA tag\n");
68260484Sobrien		return (ENOMEM);
68359024Sobrien	}
68459024Sobrien	if (bus_dmamem_alloc(sc->mfi_sense_dmat, (void **)&sc->mfi_sense,
68560484Sobrien	    BUS_DMA_NOWAIT, &sc->mfi_sense_dmamap)) {
68660484Sobrien		device_printf(sc->mfi_dev, "Cannot allocate sense memory\n");
68760484Sobrien		return (ENOMEM);
68860484Sobrien	}
68959024Sobrien	bus_dmamap_load(sc->mfi_sense_dmat, sc->mfi_sense_dmamap,
69059024Sobrien	    sc->mfi_sense, sensesz, mfi_addr_cb, &sc->mfi_sense_busaddr, 0);
69160484Sobrien	if ((error = mfi_alloc_commands(sc)) != 0)
69260484Sobrien		return (error);
69360484Sobrien
69460484Sobrien	/* Before moving the FW to operational state, check whether
69559024Sobrien	 * hostmemory is required by the FW or not
69659024Sobrien	 */
69760484Sobrien
69859024Sobrien	/* ThunderBolt MFI_IOC2 INIT */
69959024Sobrien	if (sc->mfi_flags & MFI_FLAGS_TBOLT) {
70060484Sobrien		sc->mfi_disable_intr(sc);
70159024Sobrien		mtx_lock(&sc->mfi_io_lock);
70260484Sobrien		if ((error = mfi_tbolt_init_MFI_queue(sc)) != 0) {
70360484Sobrien			device_printf(sc->mfi_dev,
70460484Sobrien			    "TB Init has failed with error %d\n",error);
70559024Sobrien			mtx_unlock(&sc->mfi_io_lock);
70659024Sobrien			return error;
70759024Sobrien		}
70877298Sobrien		mtx_unlock(&sc->mfi_io_lock);
70960484Sobrien
71060484Sobrien		if ((error = mfi_tbolt_alloc_cmd(sc)) != 0)
71160484Sobrien			return error;
71260484Sobrien		if (bus_setup_intr(sc->mfi_dev, sc->mfi_irq,
71360484Sobrien		    INTR_MPSAFE|INTR_TYPE_BIO, NULL, mfi_intr_tbolt, sc,
71460484Sobrien		    &sc->mfi_intr)) {
71560484Sobrien			device_printf(sc->mfi_dev, "Cannot set up interrupt\n");
71660484Sobrien			return (EINVAL);
71760484Sobrien		}
71860484Sobrien		sc->mfi_intr_ptr = mfi_intr_tbolt;
71960484Sobrien		sc->mfi_enable_intr(sc);
72060484Sobrien	} else {
72160484Sobrien		if ((error = mfi_comms_init(sc)) != 0)
72260484Sobrien			return (error);
72360484Sobrien
72460484Sobrien		if (bus_setup_intr(sc->mfi_dev, sc->mfi_irq,
72560484Sobrien		    INTR_MPSAFE|INTR_TYPE_BIO, NULL, mfi_intr, sc, &sc->mfi_intr)) {
72660484Sobrien			device_printf(sc->mfi_dev, "Cannot set up interrupt\n");
72760484Sobrien			return (EINVAL);
72860484Sobrien		}
729218822Sdim		sc->mfi_intr_ptr = mfi_intr;
73059024Sobrien		sc->mfi_enable_intr(sc);
73159024Sobrien	}
73277298Sobrien	if ((error = mfi_get_controller_info(sc)) != 0)
73377298Sobrien		return (error);
73477298Sobrien	sc->disableOnlineCtrlReset = 0;
73577298Sobrien
73659024Sobrien	/* Register a config hook to probe the bus for arrays */
73759024Sobrien	sc->mfi_ich.ich_func = mfi_startup;
73859024Sobrien	sc->mfi_ich.ich_arg = sc;
73959024Sobrien	if (config_intrhook_establish(&sc->mfi_ich) != 0) {
74059024Sobrien		device_printf(sc->mfi_dev, "Cannot establish configuration "
74159024Sobrien		    "hook\n");
74259024Sobrien		return (EINVAL);
74359024Sobrien	}
74459024Sobrien	mtx_lock(&sc->mfi_io_lock);
74559024Sobrien	if ((error = mfi_aen_setup(sc, 0), 0) != 0) {
74659024Sobrien		mtx_unlock(&sc->mfi_io_lock);
74759024Sobrien		return (error);
74859024Sobrien	}
74959024Sobrien	mtx_unlock(&sc->mfi_io_lock);
75059024Sobrien
75159024Sobrien	/*
75259024Sobrien	 * Register a shutdown handler.
75359024Sobrien	 */
75459024Sobrien	if ((sc->mfi_eh = EVENTHANDLER_REGISTER(shutdown_final, mfi_shutdown,
755218822Sdim	    sc, SHUTDOWN_PRI_DEFAULT)) == NULL) {
75659024Sobrien		device_printf(sc->mfi_dev, "Warning: shutdown event "
75777298Sobrien		    "registration failed\n");
75859024Sobrien	}
75959024Sobrien
760218822Sdim	/*
761218822Sdim	 * Create the control device for doing management
762218822Sdim	 */
763218822Sdim	unit = device_get_unit(sc->mfi_dev);
764218822Sdim	sc->mfi_cdev = make_dev(&mfi_cdevsw, unit, UID_ROOT, GID_OPERATOR,
765218822Sdim	    0640, "mfi%d", unit);
766218822Sdim	if (unit == 0)
767218822Sdim		make_dev_alias_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK, &dev_t,
768218822Sdim		    sc->mfi_cdev, "%s", "megaraid_sas_ioctl_node");
769218822Sdim	if (sc->mfi_cdev != NULL)
770218822Sdim		sc->mfi_cdev->si_drv1 = sc;
77177298Sobrien	SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->mfi_dev),
77259024Sobrien	    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->mfi_dev)),
77359024Sobrien	    OID_AUTO, "delete_busy_volumes", CTLFLAG_RW,
77459024Sobrien	    &sc->mfi_delete_busy_volumes, 0, "Allow removal of busy volumes");
77559024Sobrien	SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->mfi_dev),
77677298Sobrien	    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->mfi_dev)),
77777298Sobrien	    OID_AUTO, "keep_deleted_volumes", CTLFLAG_RW,
77859024Sobrien	    &sc->mfi_keep_deleted_volumes, 0,
77959024Sobrien	    "Don't detach the mfid device for a busy volume that is deleted");
78059024Sobrien
78159024Sobrien	device_add_child(sc->mfi_dev, "mfip", -1);
78259024Sobrien	bus_generic_attach(sc->mfi_dev);
78359024Sobrien
78459024Sobrien	/* Start the timeout watchdog */
78577298Sobrien	callout_init(&sc->mfi_watchdog_callout, 1);
78659024Sobrien	callout_reset(&sc->mfi_watchdog_callout, mfi_cmd_timeout * hz,
78759024Sobrien	    mfi_timeout, sc);
78859024Sobrien
78959024Sobrien	if (sc->mfi_flags & MFI_FLAGS_TBOLT) {
79059024Sobrien		mtx_lock(&sc->mfi_io_lock);
79159024Sobrien		mfi_tbolt_sync_map_info(sc);
79259024Sobrien		mtx_unlock(&sc->mfi_io_lock);
79359024Sobrien	}
79459024Sobrien
79559024Sobrien	return (0);
79659024Sobrien}
79759024Sobrien
79859024Sobrienstatic int
79959024Sobrienmfi_alloc_commands(struct mfi_softc *sc)
80077298Sobrien{
80177298Sobrien	struct mfi_command *cm;
80259024Sobrien	int i, j;
80359024Sobrien
80459024Sobrien	/*
80559024Sobrien	 * XXX Should we allocate all the commands up front, or allocate on
80659024Sobrien	 * demand later like 'aac' does?
80759024Sobrien	 */
80859024Sobrien	sc->mfi_commands = malloc(sizeof(sc->mfi_commands[0]) *
80959024Sobrien	    sc->mfi_max_fw_cmds, M_MFIBUF, M_WAITOK | M_ZERO);
81059024Sobrien
81159024Sobrien	for (i = 0; i < sc->mfi_max_fw_cmds; i++) {
81259024Sobrien		cm = &sc->mfi_commands[i];
81359024Sobrien		cm->cm_frame = (union mfi_frame *)((uintptr_t)sc->mfi_frames +
81459024Sobrien		    sc->mfi_cmd_size * i);
81559024Sobrien		cm->cm_frame_busaddr = sc->mfi_frames_busaddr +
816130561Sobrien		    sc->mfi_cmd_size * i;
81759024Sobrien		cm->cm_frame->header.context = i;
81859024Sobrien		cm->cm_sense = &sc->mfi_sense[i];
81959024Sobrien		cm->cm_sense_busaddr= sc->mfi_sense_busaddr + MFI_SENSE_LEN * i;
82059024Sobrien		cm->cm_sc = sc;
82159024Sobrien		cm->cm_index = i;
82259024Sobrien		if (bus_dmamap_create(sc->mfi_buffer_dmat, 0,
82359024Sobrien		    &cm->cm_dmamap) == 0) {
82459024Sobrien			mtx_lock(&sc->mfi_io_lock);
82560484Sobrien			mfi_release_command(cm);
82660484Sobrien			mtx_unlock(&sc->mfi_io_lock);
82759024Sobrien		} else {
82859024Sobrien			device_printf(sc->mfi_dev, "Failed to allocate %d "
82959024Sobrien			   "command blocks, only allocated %d\n",
83059024Sobrien			    sc->mfi_max_fw_cmds, i - 1);
83159024Sobrien			for (j = 0; j < i; j++) {
83259024Sobrien				cm = &sc->mfi_commands[i];
83360484Sobrien				bus_dmamap_destroy(sc->mfi_buffer_dmat,
83460484Sobrien				    cm->cm_dmamap);
83559024Sobrien			}
83659024Sobrien			free(sc->mfi_commands, M_MFIBUF);
83759024Sobrien			sc->mfi_commands = NULL;
83859024Sobrien
83959024Sobrien			return (ENOMEM);
84059024Sobrien		}
84159024Sobrien	}
84259024Sobrien
84360484Sobrien	return (0);
84460484Sobrien}
84560484Sobrien
84677298Sobrienvoid
84777298Sobrienmfi_release_command(struct mfi_command *cm)
84877298Sobrien{
84977298Sobrien	struct mfi_frame_header *hdr;
85060484Sobrien	uint32_t *hdr_data;
85177298Sobrien
85277298Sobrien	mtx_assert(&cm->cm_sc->mfi_io_lock, MA_OWNED);
85377298Sobrien
85477298Sobrien	/*
85577298Sobrien	 * Zero out the important fields of the frame, but make sure the
85660484Sobrien	 * context field is preserved.  For efficiency, handle the fields
85760484Sobrien	 * as 32 bit words.  Clear out the first S/G entry too for safety.
85860484Sobrien	 */
85960484Sobrien	hdr = &cm->cm_frame->header;
86060484Sobrien	if (cm->cm_data != NULL && hdr->sg_count) {
86160484Sobrien		cm->cm_sg->sg32[0].len = 0;
86260484Sobrien		cm->cm_sg->sg32[0].addr = 0;
86360484Sobrien	}
86460484Sobrien
86560484Sobrien	/*
86660484Sobrien	 * Command may be on other queues e.g. busy queue depending on the
86760484Sobrien	 * flow of a previous call to mfi_mapcmd, so ensure its dequeued
86859024Sobrien	 * properly
86960484Sobrien	 */
87059024Sobrien	if ((cm->cm_flags & MFI_ON_MFIQ_BUSY) != 0)
87159024Sobrien		mfi_remove_busy(cm);
87259024Sobrien	if ((cm->cm_flags & MFI_ON_MFIQ_READY) != 0)
87359024Sobrien		mfi_remove_ready(cm);
87459024Sobrien
87559024Sobrien	/* We're not expecting it to be on any other queue but check */
87659024Sobrien	if ((cm->cm_flags & MFI_ON_MFIQ_MASK) != 0) {
87759024Sobrien		panic("Command %p is still on another queue, flags = %#x",
87859024Sobrien		    cm, cm->cm_flags);
87959024Sobrien	}
88059024Sobrien
88159024Sobrien	/* tbolt cleanup */
88259024Sobrien	if ((cm->cm_flags & MFI_CMD_TBOLT) != 0) {
88359024Sobrien		mfi_tbolt_return_cmd(cm->cm_sc,
88459024Sobrien		    cm->cm_sc->mfi_cmd_pool_tbolt[cm->cm_extra_frames - 1],
88559024Sobrien		    cm);
88659024Sobrien	}
88759024Sobrien
88859024Sobrien	hdr_data = (uint32_t *)cm->cm_frame;
88959024Sobrien	hdr_data[0] = 0;	/* cmd, sense_len, cmd_status, scsi_status */
89059024Sobrien	hdr_data[1] = 0;	/* target_id, lun_id, cdb_len, sg_count */
89159024Sobrien	hdr_data[4] = 0;	/* flags, timeout */
89259024Sobrien	hdr_data[5] = 0;	/* data_len */
89359024Sobrien
89459024Sobrien	cm->cm_extra_frames = 0;
89559024Sobrien	cm->cm_flags = 0;
89659024Sobrien	cm->cm_complete = NULL;
89759024Sobrien	cm->cm_private = NULL;
89859024Sobrien	cm->cm_data = NULL;
89959024Sobrien	cm->cm_sg = 0;
90059024Sobrien	cm->cm_total_frame_size = 0;
90159024Sobrien	cm->retry_for_fw_reset = 0;
90259024Sobrien
90359024Sobrien	mfi_enqueue_free(cm);
90477298Sobrien}
90577298Sobrien
90659024Sobrienint
90777298Sobrienmfi_dcmd_command(struct mfi_softc *sc, struct mfi_command **cmp,
90877298Sobrien    uint32_t opcode, void **bufp, size_t bufsize)
90977298Sobrien{
91077298Sobrien	struct mfi_command *cm;
91177298Sobrien	struct mfi_dcmd_frame *dcmd;
91277298Sobrien	void *buf = NULL;
91359024Sobrien	uint32_t context = 0;
91477298Sobrien
91577298Sobrien	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
91677298Sobrien
91777298Sobrien	cm = mfi_dequeue_free(sc);
91877298Sobrien	if (cm == NULL)
91977298Sobrien		return (EBUSY);
92077298Sobrien
92177298Sobrien	/* Zero out the MFI frame */
92277298Sobrien	context = cm->cm_frame->header.context;
92377298Sobrien	bzero(cm->cm_frame, sizeof(union mfi_frame));
92477298Sobrien	cm->cm_frame->header.context = context;
92577298Sobrien
92659024Sobrien	if ((bufsize > 0) && (bufp != NULL)) {
92759024Sobrien		if (*bufp == NULL) {
92859024Sobrien			buf = malloc(bufsize, M_MFIBUF, M_NOWAIT|M_ZERO);
92959024Sobrien			if (buf == NULL) {
93059024Sobrien				mfi_release_command(cm);
93159024Sobrien				return (ENOMEM);
93259024Sobrien			}
93359024Sobrien			*bufp = buf;
93459024Sobrien		} else {
93559024Sobrien			buf = *bufp;
93660484Sobrien		}
93760484Sobrien	}
93860484Sobrien
93960484Sobrien	dcmd =  &cm->cm_frame->dcmd;
94077298Sobrien	bzero(dcmd->mbox, MFI_MBOX_SIZE);
94191041Sobrien	dcmd->header.cmd = MFI_CMD_DCMD;
94260484Sobrien	dcmd->header.timeout = 0;
94359024Sobrien	dcmd->header.flags = 0;
94459024Sobrien	dcmd->header.data_len = bufsize;
94559024Sobrien	dcmd->header.scsi_status = 0;
94659024Sobrien	dcmd->opcode = opcode;
94759024Sobrien	cm->cm_sg = &dcmd->sgl;
94859024Sobrien	cm->cm_total_frame_size = MFI_DCMD_FRAME_SIZE;
94959024Sobrien	cm->cm_flags = 0;
95059024Sobrien	cm->cm_data = buf;
95159024Sobrien	cm->cm_private = buf;
95259024Sobrien	cm->cm_len = bufsize;
95359024Sobrien
95459024Sobrien	*cmp = cm;
95559024Sobrien	if ((bufp != NULL) && (*bufp == NULL) && (buf != NULL))
95659024Sobrien		*bufp = buf;
95759024Sobrien	return (0);
95859024Sobrien}
95959024Sobrien
96059024Sobrienstatic int
96159024Sobrienmfi_comms_init(struct mfi_softc *sc)
96259024Sobrien{
96359024Sobrien	struct mfi_command *cm;
96459024Sobrien	struct mfi_init_frame *init;
96559024Sobrien	struct mfi_init_qinfo *qinfo;
96659024Sobrien	int error;
96759024Sobrien	uint32_t context = 0;
96859024Sobrien
96959024Sobrien	mtx_lock(&sc->mfi_io_lock);
97059024Sobrien	if ((cm = mfi_dequeue_free(sc)) == NULL) {
97159024Sobrien		mtx_unlock(&sc->mfi_io_lock);
97259024Sobrien		return (EBUSY);
97359024Sobrien	}
97459024Sobrien
97559024Sobrien	/* Zero out the MFI frame */
97659024Sobrien	context = cm->cm_frame->header.context;
97759024Sobrien	bzero(cm->cm_frame, sizeof(union mfi_frame));
97859024Sobrien	cm->cm_frame->header.context = context;
97959024Sobrien
98059024Sobrien	/*
98159024Sobrien	 * Abuse the SG list area of the frame to hold the init_qinfo
98259024Sobrien	 * object;
98359024Sobrien	 */
98459024Sobrien	init = &cm->cm_frame->init;
98559024Sobrien	qinfo = (struct mfi_init_qinfo *)((uintptr_t)init + MFI_FRAME_SIZE);
98659024Sobrien
98759024Sobrien	bzero(qinfo, sizeof(struct mfi_init_qinfo));
98859024Sobrien	qinfo->rq_entries = sc->mfi_max_fw_cmds + 1;
98959024Sobrien	qinfo->rq_addr_lo = sc->mfi_comms_busaddr +
99059024Sobrien	    offsetof(struct mfi_hwcomms, hw_reply_q);
99159024Sobrien	qinfo->pi_addr_lo = sc->mfi_comms_busaddr +
99259024Sobrien	    offsetof(struct mfi_hwcomms, hw_pi);
99359024Sobrien	qinfo->ci_addr_lo = sc->mfi_comms_busaddr +
99459024Sobrien	    offsetof(struct mfi_hwcomms, hw_ci);
99559024Sobrien
99659024Sobrien	init->header.cmd = MFI_CMD_INIT;
99759024Sobrien	init->header.data_len = sizeof(struct mfi_init_qinfo);
99860484Sobrien	init->qinfo_new_addr_lo = cm->cm_frame_busaddr + MFI_FRAME_SIZE;
99959024Sobrien	cm->cm_data = NULL;
100059024Sobrien	cm->cm_flags = MFI_CMD_POLLED;
100159024Sobrien
100259024Sobrien	if ((error = mfi_mapcmd(sc, cm)) != 0)
100359024Sobrien		device_printf(sc->mfi_dev, "failed to send init command\n");
100459024Sobrien	mfi_release_command(cm);
100559024Sobrien	mtx_unlock(&sc->mfi_io_lock);
100659024Sobrien
100759024Sobrien	return (error);
100859024Sobrien}
100959024Sobrien
101059024Sobrienstatic int
101159024Sobrienmfi_get_controller_info(struct mfi_softc *sc)
101259024Sobrien{
101359024Sobrien	struct mfi_command *cm = NULL;
101459024Sobrien	struct mfi_ctrl_info *ci = NULL;
101559024Sobrien	uint32_t max_sectors_1, max_sectors_2;
101659024Sobrien	int error;
101759024Sobrien
101860484Sobrien	mtx_lock(&sc->mfi_io_lock);
101959024Sobrien	error = mfi_dcmd_command(sc, &cm, MFI_DCMD_CTRL_GETINFO,
102059024Sobrien	    (void **)&ci, sizeof(*ci));
102159024Sobrien	if (error)
102259024Sobrien		goto out;
102359024Sobrien	cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_POLLED;
102459024Sobrien
102559024Sobrien	if ((error = mfi_mapcmd(sc, cm)) != 0) {
102659024Sobrien		device_printf(sc->mfi_dev, "Failed to get controller info\n");
102759024Sobrien		sc->mfi_max_io = (sc->mfi_max_sge - 1) * PAGE_SIZE /
102859024Sobrien		    MFI_SECTOR_LEN;
102960484Sobrien		error = 0;
103077298Sobrien		goto out;
103160484Sobrien	}
103260484Sobrien
103360484Sobrien	bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap,
103460484Sobrien	    BUS_DMASYNC_POSTREAD);
103560484Sobrien	bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap);
103660484Sobrien
103760484Sobrien	max_sectors_1 = (1 << ci->stripe_sz_ops.max) * ci->max_strips_per_io;
103860484Sobrien	max_sectors_2 = ci->max_request_size;
103960484Sobrien	sc->mfi_max_io = min(max_sectors_1, max_sectors_2);
104060484Sobrien	sc->disableOnlineCtrlReset =
104160484Sobrien	    ci->properties.OnOffProperties.disableOnlineCtrlReset;
104277298Sobrien
104360484Sobrienout:
104491041Sobrien	if (ci)
104560484Sobrien		free(ci, M_MFIBUF);
104660484Sobrien	if (cm)
104760484Sobrien		mfi_release_command(cm);
104860484Sobrien	mtx_unlock(&sc->mfi_io_lock);
104977298Sobrien	return (error);
105091041Sobrien}
105191041Sobrien
105260484Sobrienstatic int
105377298Sobrienmfi_get_log_state(struct mfi_softc *sc, struct mfi_evt_log_state **log_state)
105460484Sobrien{
105560484Sobrien	struct mfi_command *cm = NULL;
105660484Sobrien	int error;
105760484Sobrien
105860484Sobrien	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
105960484Sobrien	error = mfi_dcmd_command(sc, &cm, MFI_DCMD_CTRL_EVENT_GETINFO,
106060484Sobrien	    (void **)log_state, sizeof(**log_state));
106160484Sobrien	if (error)
106260484Sobrien		goto out;
106360484Sobrien	cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_POLLED;
106477298Sobrien
106577298Sobrien	if ((error = mfi_mapcmd(sc, cm)) != 0) {
106660484Sobrien		device_printf(sc->mfi_dev, "Failed to get log state\n");
106777298Sobrien		goto out;
106860484Sobrien	}
106960484Sobrien
107060484Sobrien	bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap,
107160484Sobrien	    BUS_DMASYNC_POSTREAD);
107260484Sobrien	bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap);
107360484Sobrien
107460484Sobrienout:
107560484Sobrien	if (cm)
107660484Sobrien		mfi_release_command(cm);
107760484Sobrien
107860484Sobrien	return (error);
107960484Sobrien}
108077298Sobrien
108177298Sobrienint
108260484Sobrienmfi_aen_setup(struct mfi_softc *sc, uint32_t seq_start)
108377298Sobrien{
108460484Sobrien	struct mfi_evt_log_state *log_state = NULL;
108560484Sobrien	union mfi_evt class_locale;
108660484Sobrien	int error = 0;
108777298Sobrien	uint32_t seq;
108860484Sobrien
108977298Sobrien	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
109060484Sobrien
109160484Sobrien	class_locale.members.reserved = 0;
109260484Sobrien	class_locale.members.locale = mfi_event_locale;
109360484Sobrien	class_locale.members.evt_class  = mfi_event_class;
109460484Sobrien
109560484Sobrien	if (seq_start == 0) {
109660484Sobrien		if ((error = mfi_get_log_state(sc, &log_state)) != 0)
109760484Sobrien			goto out;
109860484Sobrien		sc->mfi_boot_seq_num = log_state->boot_seq_num;
109960484Sobrien
110060484Sobrien		/*
110160484Sobrien		 * Walk through any events that fired since the last
110260484Sobrien		 * shutdown.
110360484Sobrien		 */
110460484Sobrien		if ((error = mfi_parse_entries(sc, log_state->shutdown_seq_num,
110560484Sobrien		    log_state->newest_seq_num)) != 0)
110660484Sobrien			goto out;
110760484Sobrien		seq = log_state->newest_seq_num;
110860484Sobrien	} else
110977298Sobrien		seq = seq_start;
111091041Sobrien	error = mfi_aen_register(sc, seq, class_locale.word);
111191041Sobrienout:
111260484Sobrien	free(log_state, M_MFIBUF);
111360484Sobrien
111477298Sobrien	return (error);
111577298Sobrien}
111660484Sobrien
111760484Sobrienint
111860484Sobrienmfi_wait_command(struct mfi_softc *sc, struct mfi_command *cm)
111960484Sobrien{
112060484Sobrien
112160484Sobrien	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
112260484Sobrien	cm->cm_complete = NULL;
112377298Sobrien
112460484Sobrien	/*
112560484Sobrien	 * MegaCli can issue a DCMD of 0.  In this case do nothing
112660484Sobrien	 * and return 0 to it as status
112760484Sobrien	 */
112860484Sobrien	if (cm->cm_frame->dcmd.opcode == 0) {
112960484Sobrien		cm->cm_frame->header.cmd_status = MFI_STAT_OK;
113060484Sobrien		cm->cm_error = 0;
113160484Sobrien		return (cm->cm_error);
113260484Sobrien	}
113360484Sobrien	mfi_enqueue_ready(cm);
113460484Sobrien	mfi_startio(sc);
113560484Sobrien	if ((cm->cm_flags & MFI_CMD_COMPLETED) == 0)
113660484Sobrien		msleep(cm, &sc->mfi_io_lock, PRIBIO, "mfiwait", 0);
113760484Sobrien	return (cm->cm_error);
113860484Sobrien}
113960484Sobrien
114060484Sobrienvoid
114177298Sobrienmfi_free(struct mfi_softc *sc)
114260484Sobrien{
114360484Sobrien	struct mfi_command *cm;
114460484Sobrien	int i;
114560484Sobrien
114660484Sobrien	callout_drain(&sc->mfi_watchdog_callout);
114760484Sobrien
114860484Sobrien	if (sc->mfi_cdev != NULL)
114960484Sobrien		destroy_dev(sc->mfi_cdev);
115060484Sobrien
115160484Sobrien	if (sc->mfi_commands != NULL) {
115277298Sobrien		for (i = 0; i < sc->mfi_max_fw_cmds; i++) {
115391041Sobrien			cm = &sc->mfi_commands[i];
115460484Sobrien			bus_dmamap_destroy(sc->mfi_buffer_dmat, cm->cm_dmamap);
115560484Sobrien		}
115660484Sobrien		free(sc->mfi_commands, M_MFIBUF);
115760484Sobrien		sc->mfi_commands = NULL;
115860484Sobrien	}
115960484Sobrien
116060484Sobrien	if (sc->mfi_intr)
116160484Sobrien		bus_teardown_intr(sc->mfi_dev, sc->mfi_irq, sc->mfi_intr);
116260484Sobrien	if (sc->mfi_irq != NULL)
116360484Sobrien		bus_release_resource(sc->mfi_dev, SYS_RES_IRQ, sc->mfi_irq_rid,
116460484Sobrien		    sc->mfi_irq);
116560484Sobrien
116660484Sobrien	if (sc->mfi_sense_busaddr != 0)
116760484Sobrien		bus_dmamap_unload(sc->mfi_sense_dmat, sc->mfi_sense_dmamap);
116860484Sobrien	if (sc->mfi_sense != NULL)
116960484Sobrien		bus_dmamem_free(sc->mfi_sense_dmat, sc->mfi_sense,
117060484Sobrien		    sc->mfi_sense_dmamap);
117160484Sobrien	if (sc->mfi_sense_dmat != NULL)
117260484Sobrien		bus_dma_tag_destroy(sc->mfi_sense_dmat);
117360484Sobrien
117460484Sobrien	if (sc->mfi_frames_busaddr != 0)
117560484Sobrien		bus_dmamap_unload(sc->mfi_frames_dmat, sc->mfi_frames_dmamap);
117660484Sobrien	if (sc->mfi_frames != NULL)
117760484Sobrien		bus_dmamem_free(sc->mfi_frames_dmat, sc->mfi_frames,
117860484Sobrien		    sc->mfi_frames_dmamap);
117960484Sobrien	if (sc->mfi_frames_dmat != NULL)
118077298Sobrien		bus_dma_tag_destroy(sc->mfi_frames_dmat);
118177298Sobrien
118260484Sobrien	if (sc->mfi_comms_busaddr != 0)
118360484Sobrien		bus_dmamap_unload(sc->mfi_comms_dmat, sc->mfi_comms_dmamap);
118460484Sobrien	if (sc->mfi_comms != NULL)
118560484Sobrien		bus_dmamem_free(sc->mfi_comms_dmat, sc->mfi_comms,
118660484Sobrien		    sc->mfi_comms_dmamap);
118760484Sobrien	if (sc->mfi_comms_dmat != NULL)
118860484Sobrien		bus_dma_tag_destroy(sc->mfi_comms_dmat);
118977298Sobrien
119060484Sobrien	/* ThunderBolt contiguous memory free here */
119160484Sobrien	if (sc->mfi_flags & MFI_FLAGS_TBOLT) {
119260484Sobrien		if (sc->mfi_tb_busaddr != 0)
119377298Sobrien			bus_dmamap_unload(sc->mfi_tb_dmat, sc->mfi_tb_dmamap);
119460484Sobrien		if (sc->request_message_pool != NULL)
119560484Sobrien			bus_dmamem_free(sc->mfi_tb_dmat, sc->request_message_pool,
119660484Sobrien			    sc->mfi_tb_dmamap);
119760484Sobrien		if (sc->mfi_tb_dmat != NULL)
119860484Sobrien			bus_dma_tag_destroy(sc->mfi_tb_dmat);
119960484Sobrien
120077298Sobrien		/* Version buffer memory free */
120160484Sobrien		/* Start LSIP200113393 */
120260484Sobrien		if (sc->verbuf_h_busaddr != 0)
120360484Sobrien			bus_dmamap_unload(sc->verbuf_h_dmat, sc->verbuf_h_dmamap);
120460484Sobrien		if (sc->verbuf != NULL)
120560484Sobrien			bus_dmamem_free(sc->verbuf_h_dmat, sc->verbuf,
120660484Sobrien			    sc->verbuf_h_dmamap);
120760484Sobrien		if (sc->verbuf_h_dmat != NULL)
120860484Sobrien			bus_dma_tag_destroy(sc->verbuf_h_dmat);
120960484Sobrien
121060484Sobrien		/* End LSIP200113393 */
121160484Sobrien		/* ThunderBolt INIT packet memory Free */
121260484Sobrien		if (sc->mfi_tb_init_busaddr != 0)
121360484Sobrien			bus_dmamap_unload(sc->mfi_tb_init_dmat,
121477298Sobrien			    sc->mfi_tb_init_dmamap);
121560484Sobrien		if (sc->mfi_tb_init != NULL)
121660484Sobrien			bus_dmamem_free(sc->mfi_tb_init_dmat, sc->mfi_tb_init,
121760484Sobrien			    sc->mfi_tb_init_dmamap);
121860484Sobrien		if (sc->mfi_tb_init_dmat != NULL)
121960484Sobrien			bus_dma_tag_destroy(sc->mfi_tb_init_dmat);
122060484Sobrien
122160484Sobrien		/* ThunderBolt IOC Init Desc memory free here */
122260484Sobrien		if (sc->mfi_tb_ioc_init_busaddr != 0)
122360484Sobrien			bus_dmamap_unload(sc->mfi_tb_ioc_init_dmat,
122460484Sobrien			    sc->mfi_tb_ioc_init_dmamap);
122560484Sobrien		if (sc->mfi_tb_ioc_init_desc != NULL)
122660484Sobrien			bus_dmamem_free(sc->mfi_tb_ioc_init_dmat,
122760484Sobrien			    sc->mfi_tb_ioc_init_desc,
122860484Sobrien			    sc->mfi_tb_ioc_init_dmamap);
122960484Sobrien		if (sc->mfi_tb_ioc_init_dmat != NULL)
123077298Sobrien			bus_dma_tag_destroy(sc->mfi_tb_ioc_init_dmat);
123160484Sobrien		if (sc->mfi_cmd_pool_tbolt != NULL) {
123260484Sobrien			for (int i = 0; i < sc->mfi_max_fw_cmds; i++) {
123360484Sobrien				if (sc->mfi_cmd_pool_tbolt[i] != NULL) {
123460484Sobrien					free(sc->mfi_cmd_pool_tbolt[i],
123560484Sobrien					    M_MFIBUF);
123660484Sobrien					sc->mfi_cmd_pool_tbolt[i] = NULL;
123760484Sobrien				}
123860484Sobrien			}
123960484Sobrien			free(sc->mfi_cmd_pool_tbolt, M_MFIBUF);
124060484Sobrien			sc->mfi_cmd_pool_tbolt = NULL;
124160484Sobrien		}
124277298Sobrien		if (sc->request_desc_pool != NULL) {
124360484Sobrien			free(sc->request_desc_pool, M_MFIBUF);
124460484Sobrien			sc->request_desc_pool = NULL;
124560484Sobrien		}
124660484Sobrien	}
124777298Sobrien	if (sc->mfi_buffer_dmat != NULL)
124860484Sobrien		bus_dma_tag_destroy(sc->mfi_buffer_dmat);
124960484Sobrien	if (sc->mfi_parent_dmat != NULL)
125060484Sobrien		bus_dma_tag_destroy(sc->mfi_parent_dmat);
125160484Sobrien
125260484Sobrien	if (mtx_initialized(&sc->mfi_io_lock)) {
125360484Sobrien		mtx_destroy(&sc->mfi_io_lock);
125460484Sobrien		sx_destroy(&sc->mfi_config_lock);
125560484Sobrien	}
125660484Sobrien
125760484Sobrien	return;
125860484Sobrien}
125960484Sobrien
126060484Sobrienstatic void
126160484Sobrienmfi_startup(void *arg)
126260484Sobrien{
126360484Sobrien	struct mfi_softc *sc;
126477298Sobrien
126560484Sobrien	sc = (struct mfi_softc *)arg;
126660484Sobrien
126760484Sobrien	sc->mfi_enable_intr(sc);
126860484Sobrien	sx_xlock(&sc->mfi_config_lock);
126960484Sobrien	mtx_lock(&sc->mfi_io_lock);
127060484Sobrien	mfi_ldprobe(sc);
127160484Sobrien	if (sc->mfi_flags & MFI_FLAGS_SKINNY)
127260484Sobrien	    mfi_syspdprobe(sc);
127360484Sobrien	mtx_unlock(&sc->mfi_io_lock);
127460484Sobrien	sx_xunlock(&sc->mfi_config_lock);
127560484Sobrien
127660484Sobrien	config_intrhook_disestablish(&sc->mfi_ich);
127760484Sobrien}
127877298Sobrien
127960484Sobrienstatic void
128060484Sobrienmfi_intr(void *arg)
128160484Sobrien{
128260484Sobrien	struct mfi_softc *sc;
128360484Sobrien	struct mfi_command *cm;
128460484Sobrien	uint32_t pi, ci, context;
128560484Sobrien
128660484Sobrien	sc = (struct mfi_softc *)arg;
128777298Sobrien
128860484Sobrien	if (sc->mfi_check_clear_intr(sc))
128960484Sobrien		return;
129060484Sobrien
129160484Sobrienrestart:
129260484Sobrien	pi = sc->mfi_comms->hw_pi;
129360484Sobrien	ci = sc->mfi_comms->hw_ci;
129460484Sobrien	mtx_lock(&sc->mfi_io_lock);
129560484Sobrien	while (ci != pi) {
129660484Sobrien		context = sc->mfi_comms->hw_reply_q[ci];
129760484Sobrien		if (context < sc->mfi_max_fw_cmds) {
129860484Sobrien			cm = &sc->mfi_commands[context];
129960484Sobrien			mfi_remove_busy(cm);
130060484Sobrien			cm->cm_error = 0;
130160484Sobrien			mfi_complete(sc, cm);
130260484Sobrien		}
130360484Sobrien		if (++ci == (sc->mfi_max_fw_cmds + 1))
130460484Sobrien			ci = 0;
130560484Sobrien	}
130660484Sobrien
130759024Sobrien	sc->mfi_comms->hw_ci = ci;
130859024Sobrien
130959024Sobrien	/* Give defered I/O a chance to run */
131059024Sobrien	sc->mfi_flags &= ~MFI_FLAGS_QFRZN;
131159024Sobrien	mfi_startio(sc);
131259024Sobrien	mtx_unlock(&sc->mfi_io_lock);
131359024Sobrien
131460484Sobrien	/*
131559024Sobrien	 * Dummy read to flush the bus; this ensures that the indexes are up
131659024Sobrien	 * to date.  Restart processing if more commands have come it.
131760484Sobrien	 */
1318218822Sdim	(void)sc->mfi_read_fw_status(sc);
1319218822Sdim	if (pi != sc->mfi_comms->hw_pi)
132059024Sobrien		goto restart;
132159024Sobrien
132259024Sobrien	return;
1323218822Sdim}
132459024Sobrien
132559024Sobrienint
132659024Sobrienmfi_shutdown(struct mfi_softc *sc)
132759024Sobrien{
132859024Sobrien	struct mfi_dcmd_frame *dcmd;
132959024Sobrien	struct mfi_command *cm;
133060484Sobrien	int error;
133159024Sobrien
133259024Sobrien
133359024Sobrien	if (sc->mfi_aen_cm != NULL) {
133459024Sobrien		sc->cm_aen_abort = 1;
133559024Sobrien		mfi_abort(sc, &sc->mfi_aen_cm);
133659024Sobrien	}
133759024Sobrien
133859024Sobrien	if (sc->mfi_map_sync_cm != NULL) {
133959024Sobrien		sc->cm_map_abort = 1;
134059024Sobrien		mfi_abort(sc, &sc->mfi_map_sync_cm);
134159024Sobrien	}
134259024Sobrien
134359024Sobrien	mtx_lock(&sc->mfi_io_lock);
134459024Sobrien	error = mfi_dcmd_command(sc, &cm, MFI_DCMD_CTRL_SHUTDOWN, NULL, 0);
134560484Sobrien	if (error) {
134659024Sobrien		mtx_unlock(&sc->mfi_io_lock);
134759024Sobrien		return (error);
134859024Sobrien	}
134959024Sobrien
135059024Sobrien	dcmd = &cm->cm_frame->dcmd;
135177298Sobrien	dcmd->header.flags = MFI_FRAME_DIR_NONE;
135259024Sobrien	cm->cm_flags = MFI_CMD_POLLED;
135359024Sobrien	cm->cm_data = NULL;
135459024Sobrien
135560484Sobrien	if ((error = mfi_mapcmd(sc, cm)) != 0)
135660484Sobrien		device_printf(sc->mfi_dev, "Failed to shutdown controller\n");
135760484Sobrien
135877298Sobrien	mfi_release_command(cm);
135959024Sobrien	mtx_unlock(&sc->mfi_io_lock);
136060484Sobrien	return (error);
136160484Sobrien}
136259024Sobrien
136359024Sobrienstatic void
136460484Sobrienmfi_syspdprobe(struct mfi_softc *sc)
136560484Sobrien{
136677298Sobrien	struct mfi_frame_header *hdr;
136759024Sobrien	struct mfi_command *cm = NULL;
136859024Sobrien	struct mfi_pd_list *pdlist = NULL;
136959024Sobrien	struct mfi_system_pd *syspd, *tmp;
137077298Sobrien	struct mfi_system_pending *syspd_pend;
137159024Sobrien	int error, i, found;
137277298Sobrien
137359024Sobrien	sx_assert(&sc->mfi_config_lock, SA_XLOCKED);
137459024Sobrien	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
137559024Sobrien	/* Add SYSTEM PD's */
137659024Sobrien	error = mfi_dcmd_command(sc, &cm, MFI_DCMD_PD_LIST_QUERY,
137777298Sobrien	    (void **)&pdlist, sizeof(*pdlist));
137859024Sobrien	if (error) {
137959024Sobrien		device_printf(sc->mfi_dev,
138059024Sobrien		    "Error while forming SYSTEM PD list\n");
138160484Sobrien		goto out;
138259024Sobrien	}
138377298Sobrien
138459024Sobrien	cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_POLLED;
138560484Sobrien	cm->cm_frame->dcmd.mbox[0] = MR_PD_QUERY_TYPE_EXPOSED_TO_HOST;
138659024Sobrien	cm->cm_frame->dcmd.mbox[1] = 0;
138759024Sobrien	if (mfi_mapcmd(sc, cm) != 0) {
138859024Sobrien		device_printf(sc->mfi_dev,
138959024Sobrien		    "Failed to get syspd device listing\n");
139059024Sobrien		goto out;
139160484Sobrien	}
139259024Sobrien	bus_dmamap_sync(sc->mfi_buffer_dmat,cm->cm_dmamap,
139359024Sobrien	    BUS_DMASYNC_POSTREAD);
139459024Sobrien	bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap);
139559024Sobrien	hdr = &cm->cm_frame->header;
139659024Sobrien	if (hdr->cmd_status != MFI_STAT_OK) {
139759024Sobrien		device_printf(sc->mfi_dev,
139859024Sobrien		    "MFI_DCMD_PD_LIST_QUERY failed %x\n", hdr->cmd_status);
139959024Sobrien		goto out;
140059024Sobrien	}
140159024Sobrien	/* Get each PD and add it to the system */
140259024Sobrien	for (i = 0; i < pdlist->count; i++) {
140359024Sobrien		if (pdlist->addr[i].device_id ==
140459024Sobrien		    pdlist->addr[i].encl_device_id)
140559024Sobrien			continue;
140659024Sobrien		found = 0;
140760484Sobrien		TAILQ_FOREACH(syspd, &sc->mfi_syspd_tqh, pd_link) {
140859024Sobrien			if (syspd->pd_id == pdlist->addr[i].device_id)
140960484Sobrien				found = 1;
141089857Sobrien		}
141160484Sobrien		TAILQ_FOREACH(syspd_pend, &sc->mfi_syspd_pend_tqh, pd_link) {
141260484Sobrien			if (syspd_pend->pd_id == pdlist->addr[i].device_id)
141360484Sobrien				found = 1;
141489857Sobrien		}
141560484Sobrien		if (found == 0)
141659024Sobrien			mfi_add_sys_pd(sc, pdlist->addr[i].device_id);
141759024Sobrien	}
141859024Sobrien	/* Delete SYSPD's whose state has been changed */
141959024Sobrien	TAILQ_FOREACH_SAFE(syspd, &sc->mfi_syspd_tqh, pd_link, tmp) {
142059024Sobrien		found = 0;
142159024Sobrien		for (i = 0; i < pdlist->count; i++) {
142259024Sobrien			if (syspd->pd_id == pdlist->addr[i].device_id) {
142359024Sobrien				found = 1;
142477298Sobrien				break;
142559024Sobrien			}
142659024Sobrien		}
142759024Sobrien		if (found == 0) {
142859024Sobrien			printf("DELETE\n");
142959024Sobrien			mtx_unlock(&sc->mfi_io_lock);
143059024Sobrien			mtx_lock(&Giant);
1431218822Sdim			device_delete_child(sc->mfi_dev, syspd->pd_dev);
1432218822Sdim			mtx_unlock(&Giant);
1433218822Sdim			mtx_lock(&sc->mfi_io_lock);
143459024Sobrien		}
143559024Sobrien	}
143659024Sobrienout:
143759024Sobrien	if (pdlist)
143859024Sobrien	    free(pdlist, M_MFIBUF);
143960484Sobrien	if (cm)
144060484Sobrien	    mfi_release_command(cm);
144159024Sobrien
144259024Sobrien	return;
144359024Sobrien}
144459024Sobrien
144559024Sobrienstatic void
144659024Sobrienmfi_ldprobe(struct mfi_softc *sc)
144759024Sobrien{
144859024Sobrien	struct mfi_frame_header *hdr;
144959024Sobrien	struct mfi_command *cm = NULL;
145059024Sobrien	struct mfi_ld_list *list = NULL;
145159024Sobrien	struct mfi_disk *ld;
145259024Sobrien	struct mfi_disk_pending *ld_pend;
145359024Sobrien	int error, i;
145459024Sobrien
145577298Sobrien	sx_assert(&sc->mfi_config_lock, SA_XLOCKED);
145677298Sobrien	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
145759024Sobrien
145859024Sobrien	error = mfi_dcmd_command(sc, &cm, MFI_DCMD_LD_GET_LIST,
145959024Sobrien	    (void **)&list, sizeof(*list));
146059024Sobrien	if (error)
146159024Sobrien		goto out;
146259024Sobrien
146359024Sobrien	cm->cm_flags = MFI_CMD_DATAIN;
146459024Sobrien	if (mfi_wait_command(sc, cm) != 0) {
146559024Sobrien		device_printf(sc->mfi_dev, "Failed to get device listing\n");
146659024Sobrien		goto out;
146759024Sobrien	}
146859024Sobrien
146959024Sobrien	hdr = &cm->cm_frame->header;
147059024Sobrien	if (hdr->cmd_status != MFI_STAT_OK) {
147159024Sobrien		device_printf(sc->mfi_dev, "MFI_DCMD_LD_GET_LIST failed %x\n",
147259024Sobrien		    hdr->cmd_status);
147359024Sobrien		goto out;
147459024Sobrien	}
147560484Sobrien
147659024Sobrien	for (i = 0; i < list->ld_count; i++) {
147759024Sobrien		TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link) {
147859024Sobrien			if (ld->ld_id == list->ld_list[i].ld.v.target_id)
147977298Sobrien				goto skip_add;
148077298Sobrien		}
148159024Sobrien		TAILQ_FOREACH(ld_pend, &sc->mfi_ld_pend_tqh, ld_link) {
148259024Sobrien			if (ld_pend->ld_id == list->ld_list[i].ld.v.target_id)
148377298Sobrien				goto skip_add;
148477298Sobrien		}
148559024Sobrien		mfi_add_ld(sc, list->ld_list[i].ld.v.target_id);
148659024Sobrien	skip_add:;
148759024Sobrien	}
148859024Sobrienout:
148959024Sobrien	if (list)
149059024Sobrien		free(list, M_MFIBUF);
149160484Sobrien	if (cm)
149259024Sobrien		mfi_release_command(cm);
149359024Sobrien
149459024Sobrien	return;
149559024Sobrien}
149660484Sobrien
149759024Sobrien/*
149859024Sobrien * The timestamp is the number of seconds since 00:00 Jan 1, 2000.  If
149959024Sobrien * the bits in 24-31 are all set, then it is the number of seconds since
150059024Sobrien * boot.
150159024Sobrien */
150259024Sobrienstatic const char *
150359024Sobrienformat_timestamp(uint32_t timestamp)
150459024Sobrien{
150577298Sobrien	static char buffer[32];
150677298Sobrien
150777298Sobrien	if ((timestamp & 0xff000000) == 0xff000000)
150877298Sobrien		snprintf(buffer, sizeof(buffer), "boot + %us", timestamp &
150977298Sobrien		    0x00ffffff);
151077298Sobrien	else
151177298Sobrien		snprintf(buffer, sizeof(buffer), "%us", timestamp);
151277298Sobrien	return (buffer);
151377298Sobrien}
151477298Sobrien
151577298Sobrienstatic const char *
151677298Sobrienformat_class(int8_t class)
151777298Sobrien{
151877298Sobrien	static char buffer[6];
151977298Sobrien
152077298Sobrien	switch (class) {
152177298Sobrien	case MFI_EVT_CLASS_DEBUG:
152277298Sobrien		return ("debug");
152359024Sobrien	case MFI_EVT_CLASS_PROGRESS:
152459024Sobrien		return ("progress");
152559024Sobrien	case MFI_EVT_CLASS_INFO:
152659024Sobrien		return ("info");
152759024Sobrien	case MFI_EVT_CLASS_WARNING:
152859024Sobrien		return ("WARN");
152959024Sobrien	case MFI_EVT_CLASS_CRITICAL:
153059024Sobrien		return ("CRIT");
153159024Sobrien	case MFI_EVT_CLASS_FATAL:
153260484Sobrien		return ("FATAL");
153359024Sobrien	case MFI_EVT_CLASS_DEAD:
153459024Sobrien		return ("DEAD");
153559024Sobrien	default:
153659024Sobrien		snprintf(buffer, sizeof(buffer), "%d", class);
153759024Sobrien		return (buffer);
153859024Sobrien	}
153959024Sobrien}
154060484Sobrien
154159024Sobrienstatic void
154259024Sobrienmfi_decode_evt(struct mfi_softc *sc, struct mfi_evt_detail *detail)
154359024Sobrien{
154459024Sobrien	struct mfi_system_pd *syspd = NULL;
154560484Sobrien
154659024Sobrien	device_printf(sc->mfi_dev, "%d (%s/0x%04x/%s) - %s\n", detail->seq,
154759024Sobrien	    format_timestamp(detail->time), detail->evt_class.members.locale,
154859024Sobrien	    format_class(detail->evt_class.members.evt_class),
154959024Sobrien	    detail->description);
155059024Sobrien
155159024Sobrien        /* Don't act on old AEN's or while shutting down */
155259024Sobrien        if (detail->seq < sc->mfi_boot_seq_num || sc->mfi_detaching)
155359024Sobrien                return;
155459024Sobrien
155559024Sobrien	switch (detail->arg_type) {
155659024Sobrien	case MR_EVT_ARGS_NONE:
155759024Sobrien		if (detail->code == MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED) {
155859024Sobrien		    device_printf(sc->mfi_dev, "HostBus scan raised\n");
155977298Sobrien			if (mfi_detect_jbod_change) {
156059024Sobrien				/*
156159024Sobrien				 * Probe for new SYSPD's and Delete
156259024Sobrien				 * invalid SYSPD's
156359024Sobrien				 */
156459024Sobrien				sx_xlock(&sc->mfi_config_lock);
156559024Sobrien				mtx_lock(&sc->mfi_io_lock);
156659024Sobrien				mfi_syspdprobe(sc);
156759024Sobrien				mtx_unlock(&sc->mfi_io_lock);
156859024Sobrien				sx_xunlock(&sc->mfi_config_lock);
156959024Sobrien			}
157059024Sobrien		}
157159024Sobrien		break;
157259024Sobrien	case MR_EVT_ARGS_LD_STATE:
157360484Sobrien		/* During load time driver reads all the events starting
157459024Sobrien		 * from the one that has been logged after shutdown. Avoid
157559024Sobrien		 * these old events.
157659024Sobrien		 */
157759024Sobrien		if (detail->args.ld_state.new_state == MFI_LD_STATE_OFFLINE ) {
157859024Sobrien			/* Remove the LD */
157959024Sobrien			struct mfi_disk *ld;
158059024Sobrien			TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link) {
158159024Sobrien				if (ld->ld_id ==
158259024Sobrien				    detail->args.ld_state.ld.target_id)
158359024Sobrien					break;
158459024Sobrien			}
158560484Sobrien			/*
158659024Sobrien			Fix: for kernel panics when SSCD is removed
158759024Sobrien			KASSERT(ld != NULL, ("volume dissappeared"));
158859024Sobrien			*/
1589218822Sdim			if (ld != NULL) {
1590218822Sdim				mtx_lock(&Giant);
1591218822Sdim				device_delete_child(sc->mfi_dev, ld->ld_dev);
1592218822Sdim				mtx_unlock(&Giant);
1593218822Sdim			}
1594218822Sdim		}
1595218822Sdim		break;
1596218822Sdim	case MR_EVT_ARGS_PD:
1597218822Sdim		if (detail->code == MR_EVT_PD_REMOVED) {
1598218822Sdim			if (mfi_detect_jbod_change) {
1599218822Sdim				/*
1600218822Sdim				 * If the removed device is a SYSPD then
1601218822Sdim				 * delete it
1602218822Sdim				 */
1603218822Sdim				TAILQ_FOREACH(syspd, &sc->mfi_syspd_tqh,
1604218822Sdim				    pd_link) {
1605218822Sdim					if (syspd->pd_id ==
1606218822Sdim					    detail->args.pd.device_id) {
1607218822Sdim						mtx_lock(&Giant);
1608218822Sdim						device_delete_child(
1609218822Sdim						    sc->mfi_dev,
1610218822Sdim						    syspd->pd_dev);
1611218822Sdim						mtx_unlock(&Giant);
1612218822Sdim						break;
1613218822Sdim					}
1614218822Sdim				}
1615218822Sdim			}
1616218822Sdim		}
1617218822Sdim		if (detail->code == MR_EVT_PD_INSERTED) {
1618218822Sdim			if (mfi_detect_jbod_change) {
1619218822Sdim				/* Probe for new SYSPD's */
1620218822Sdim				sx_xlock(&sc->mfi_config_lock);
1621218822Sdim				mtx_lock(&sc->mfi_io_lock);
1622218822Sdim				mfi_syspdprobe(sc);
1623218822Sdim				mtx_unlock(&sc->mfi_io_lock);
1624218822Sdim				sx_xunlock(&sc->mfi_config_lock);
162559024Sobrien			}
162659024Sobrien		}
162777298Sobrien		if (sc->mfi_cam_rescan_cb != NULL &&
162859024Sobrien		    (detail->code == MR_EVT_PD_INSERTED ||
162959024Sobrien		    detail->code == MR_EVT_PD_REMOVED)) {
163059024Sobrien			sc->mfi_cam_rescan_cb(sc, detail->args.pd.device_id);
163177298Sobrien		}
163259024Sobrien		break;
163359024Sobrien	}
163459024Sobrien}
163559024Sobrien
163659024Sobrienstatic void
163759024Sobrienmfi_queue_evt(struct mfi_softc *sc, struct mfi_evt_detail *detail)
163859024Sobrien{
163959024Sobrien	struct mfi_evt_queue_elm *elm;
164059024Sobrien
164159024Sobrien	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
164259024Sobrien	elm = malloc(sizeof(*elm), M_MFIBUF, M_NOWAIT|M_ZERO);
164359024Sobrien	if (elm == NULL)
164459024Sobrien		return;
164577298Sobrien	memcpy(&elm->detail, detail, sizeof(*detail));
164659024Sobrien	TAILQ_INSERT_TAIL(&sc->mfi_evt_queue, elm, link);
164759024Sobrien	taskqueue_enqueue(taskqueue_swi, &sc->mfi_evt_task);
164859024Sobrien}
164959024Sobrien
165060484Sobrienstatic void
165159024Sobrienmfi_handle_evt(void *context, int pending)
165277298Sobrien{
165377298Sobrien	TAILQ_HEAD(,mfi_evt_queue_elm) queue;
165477298Sobrien	struct mfi_softc *sc;
165577298Sobrien	struct mfi_evt_queue_elm *elm;
165677298Sobrien
165777298Sobrien	sc = context;
165877298Sobrien	TAILQ_INIT(&queue);
165977298Sobrien	mtx_lock(&sc->mfi_io_lock);
166077298Sobrien	TAILQ_CONCAT(&queue, &sc->mfi_evt_queue, link);
166159024Sobrien	mtx_unlock(&sc->mfi_io_lock);
166259024Sobrien	while ((elm = TAILQ_FIRST(&queue)) != NULL) {
166359024Sobrien		TAILQ_REMOVE(&queue, elm, link);
166459024Sobrien		mfi_decode_evt(sc, &elm->detail);
166559024Sobrien		free(elm, M_MFIBUF);
166659024Sobrien	}
166759024Sobrien}
166859024Sobrien
166959024Sobrienstatic int
167077298Sobrienmfi_aen_register(struct mfi_softc *sc, int seq, int locale)
167159024Sobrien{
167259024Sobrien	struct mfi_command *cm;
167359024Sobrien	struct mfi_dcmd_frame *dcmd;
167459024Sobrien	union mfi_evt current_aen, prior_aen;
167559024Sobrien	struct mfi_evt_detail *ed = NULL;
167659024Sobrien	int error = 0;
167759024Sobrien
167859024Sobrien	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
167959024Sobrien
168089857Sobrien	current_aen.word = locale;
168159024Sobrien	if (sc->mfi_aen_cm != NULL) {
168259024Sobrien		prior_aen.word =
168359024Sobrien		    ((uint32_t *)&sc->mfi_aen_cm->cm_frame->dcmd.mbox)[1];
168489857Sobrien		if (prior_aen.members.evt_class <= current_aen.members.evt_class &&
168559024Sobrien		    !((prior_aen.members.locale & current_aen.members.locale)
168659024Sobrien		    ^current_aen.members.locale)) {
168759024Sobrien			return (0);
168859024Sobrien		} else {
168959024Sobrien			prior_aen.members.locale |= current_aen.members.locale;
169059024Sobrien			if (prior_aen.members.evt_class
169159024Sobrien			    < current_aen.members.evt_class)
169259024Sobrien				current_aen.members.evt_class =
169359024Sobrien				    prior_aen.members.evt_class;
169460484Sobrien			mfi_abort(sc, &sc->mfi_aen_cm);
169559024Sobrien		}
169659024Sobrien	}
169759024Sobrien
169859024Sobrien	error = mfi_dcmd_command(sc, &cm, MFI_DCMD_CTRL_EVENT_WAIT,
169959024Sobrien	    (void **)&ed, sizeof(*ed));
170059024Sobrien	if (error)
170159024Sobrien		goto out;
170260484Sobrien
170359024Sobrien	dcmd = &cm->cm_frame->dcmd;
170459024Sobrien	((uint32_t *)&dcmd->mbox)[0] = seq;
170559024Sobrien	((uint32_t *)&dcmd->mbox)[1] = locale;
170659024Sobrien	cm->cm_flags = MFI_CMD_DATAIN;
170759024Sobrien	cm->cm_complete = mfi_aen_complete;
170859024Sobrien
170959024Sobrien	sc->last_seq_num = seq;
171059024Sobrien	sc->mfi_aen_cm = cm;
171159024Sobrien
171260484Sobrien	mfi_enqueue_ready(cm);
171359024Sobrien	mfi_startio(sc);
171459024Sobrien
171577298Sobrienout:
171659024Sobrien	return (error);
171759024Sobrien}
171859024Sobrien
171959024Sobrienstatic void
172059024Sobrienmfi_aen_complete(struct mfi_command *cm)
172159024Sobrien{
172259024Sobrien	struct mfi_frame_header *hdr;
172359024Sobrien	struct mfi_softc *sc;
172459024Sobrien	struct mfi_evt_detail *detail;
172559024Sobrien	struct mfi_aen *mfi_aen_entry, *tmp;
172659024Sobrien	int seq = 0, aborted = 0;
172759024Sobrien
172859024Sobrien	sc = cm->cm_sc;
172959024Sobrien	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
173059024Sobrien
173159024Sobrien	if (sc->mfi_aen_cm == NULL)
173259024Sobrien		return;
173359024Sobrien
173459024Sobrien	hdr = &cm->cm_frame->header;
173559024Sobrien
173659024Sobrien	if (sc->cm_aen_abort ||
173759024Sobrien	    hdr->cmd_status == MFI_STAT_INVALID_STATUS) {
173859024Sobrien		sc->cm_aen_abort = 0;
173959024Sobrien		aborted = 1;
174059024Sobrien	} else {
174159024Sobrien		sc->mfi_aen_triggered = 1;
174259024Sobrien		if (sc->mfi_poll_waiting) {
174359024Sobrien			sc->mfi_poll_waiting = 0;
174459024Sobrien			selwakeup(&sc->mfi_select);
174559024Sobrien		}
174659024Sobrien		detail = cm->cm_data;
174759024Sobrien		mfi_queue_evt(sc, detail);
174859024Sobrien		seq = detail->seq + 1;
174959024Sobrien		TAILQ_FOREACH_SAFE(mfi_aen_entry, &sc->mfi_aen_pids, aen_link,
175059024Sobrien		    tmp) {
175159024Sobrien			TAILQ_REMOVE(&sc->mfi_aen_pids, mfi_aen_entry,
175259024Sobrien			    aen_link);
175359024Sobrien			PROC_LOCK(mfi_aen_entry->p);
175459024Sobrien			kern_psignal(mfi_aen_entry->p, SIGIO);
175559024Sobrien			PROC_UNLOCK(mfi_aen_entry->p);
175659024Sobrien			free(mfi_aen_entry, M_MFIBUF);
175759024Sobrien		}
175859024Sobrien	}
175959024Sobrien
176059024Sobrien	free(cm->cm_data, M_MFIBUF);
176159024Sobrien	wakeup(&sc->mfi_aen_cm);
176259024Sobrien	sc->mfi_aen_cm = NULL;
176359024Sobrien	mfi_release_command(cm);
176459024Sobrien
176559024Sobrien	/* set it up again so the driver can catch more events */
176659024Sobrien	if (!aborted)
176759024Sobrien		mfi_aen_setup(sc, seq);
176859024Sobrien}
176959024Sobrien
177059024Sobrien#define MAX_EVENTS 15
177159024Sobrien
177259024Sobrienstatic int
177359024Sobrienmfi_parse_entries(struct mfi_softc *sc, int start_seq, int stop_seq)
177459024Sobrien{
177559024Sobrien	struct mfi_command *cm;
177659024Sobrien	struct mfi_dcmd_frame *dcmd;
177759024Sobrien	struct mfi_evt_list *el;
177859024Sobrien	union mfi_evt class_locale;
177959024Sobrien	int error, i, seq, size;
178059024Sobrien
178159024Sobrien	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
178259024Sobrien
178359024Sobrien	class_locale.members.reserved = 0;
178459024Sobrien	class_locale.members.locale = mfi_event_locale;
178559024Sobrien	class_locale.members.evt_class  = mfi_event_class;
178659024Sobrien
178759024Sobrien	size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail)
178859024Sobrien		* (MAX_EVENTS - 1);
178959024Sobrien	el = malloc(size, M_MFIBUF, M_NOWAIT | M_ZERO);
179059024Sobrien	if (el == NULL)
179159024Sobrien		return (ENOMEM);
179259024Sobrien
179359024Sobrien	for (seq = start_seq;;) {
179459024Sobrien		if ((cm = mfi_dequeue_free(sc)) == NULL) {
179559024Sobrien			free(el, M_MFIBUF);
179659024Sobrien			return (EBUSY);
179759024Sobrien		}
179859024Sobrien
179959024Sobrien		dcmd = &cm->cm_frame->dcmd;
180059024Sobrien		bzero(dcmd->mbox, MFI_MBOX_SIZE);
180159024Sobrien		dcmd->header.cmd = MFI_CMD_DCMD;
180259024Sobrien		dcmd->header.timeout = 0;
180359024Sobrien		dcmd->header.data_len = size;
180459024Sobrien		dcmd->opcode = MFI_DCMD_CTRL_EVENT_GET;
180559024Sobrien		((uint32_t *)&dcmd->mbox)[0] = seq;
180659024Sobrien		((uint32_t *)&dcmd->mbox)[1] = class_locale.word;
180759024Sobrien		cm->cm_sg = &dcmd->sgl;
180859024Sobrien		cm->cm_total_frame_size = MFI_DCMD_FRAME_SIZE;
180959024Sobrien		cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_POLLED;
181059024Sobrien		cm->cm_data = el;
181159024Sobrien		cm->cm_len = size;
181259024Sobrien
181359024Sobrien		if ((error = mfi_mapcmd(sc, cm)) != 0) {
181459024Sobrien			device_printf(sc->mfi_dev,
181559024Sobrien			    "Failed to get controller entries\n");
181659024Sobrien			mfi_release_command(cm);
181759024Sobrien			break;
181859024Sobrien		}
181959024Sobrien
182059024Sobrien		bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap,
182159024Sobrien		    BUS_DMASYNC_POSTREAD);
182259024Sobrien		bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap);
182359024Sobrien
182459024Sobrien		if (dcmd->header.cmd_status == MFI_STAT_NOT_FOUND) {
182559024Sobrien			mfi_release_command(cm);
182659024Sobrien			break;
182759024Sobrien		}
182859024Sobrien		if (dcmd->header.cmd_status != MFI_STAT_OK) {
182959024Sobrien			device_printf(sc->mfi_dev,
183059024Sobrien			    "Error %d fetching controller entries\n",
183159024Sobrien			    dcmd->header.cmd_status);
183259024Sobrien			mfi_release_command(cm);
183359024Sobrien			error = EIO;
183459024Sobrien			break;
183559024Sobrien		}
183659024Sobrien		mfi_release_command(cm);
183759024Sobrien
183859024Sobrien		for (i = 0; i < el->count; i++) {
183959024Sobrien			/*
184059024Sobrien			 * If this event is newer than 'stop_seq' then
184159024Sobrien			 * break out of the loop.  Note that the log
184259024Sobrien			 * is a circular buffer so we have to handle
184359024Sobrien			 * the case that our stop point is earlier in
184459024Sobrien			 * the buffer than our start point.
184559024Sobrien			 */
184659024Sobrien			if (el->event[i].seq >= stop_seq) {
184759024Sobrien				if (start_seq <= stop_seq)
184859024Sobrien					break;
184959024Sobrien				else if (el->event[i].seq < start_seq)
185059024Sobrien					break;
185159024Sobrien			}
185259024Sobrien			mfi_queue_evt(sc, &el->event[i]);
185359024Sobrien		}
185459024Sobrien		seq = el->event[el->count - 1].seq + 1;
185559024Sobrien	}
185659024Sobrien
185759024Sobrien	free(el, M_MFIBUF);
185859024Sobrien	return (error);
185959024Sobrien}
186077298Sobrien
1861130561Sobrienstatic int
186259024Sobrienmfi_add_ld(struct mfi_softc *sc, int id)
1863218822Sdim{
1864218822Sdim	struct mfi_command *cm;
1865130561Sobrien	struct mfi_dcmd_frame *dcmd = NULL;
1866130561Sobrien	struct mfi_ld_info *ld_info = NULL;
1867130561Sobrien	struct mfi_disk_pending *ld_pend;
1868130561Sobrien	int error;
1869130561Sobrien
1870130561Sobrien	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
1871130561Sobrien
1872130561Sobrien	ld_pend = malloc(sizeof(*ld_pend), M_MFIBUF, M_NOWAIT | M_ZERO);
1873218822Sdim	if (ld_pend != NULL) {
1874218822Sdim		ld_pend->ld_id = id;
1875218822Sdim		TAILQ_INSERT_TAIL(&sc->mfi_ld_pend_tqh, ld_pend, ld_link);
1876130561Sobrien	}
1877130561Sobrien
1878130561Sobrien	error = mfi_dcmd_command(sc, &cm, MFI_DCMD_LD_GET_INFO,
1879130561Sobrien	    (void **)&ld_info, sizeof(*ld_info));
1880130561Sobrien	if (error) {
1881130561Sobrien		device_printf(sc->mfi_dev,
1882130561Sobrien		    "Failed to allocate for MFI_DCMD_LD_GET_INFO %d\n", error);
1883218822Sdim		if (ld_info)
1884218822Sdim			free(ld_info, M_MFIBUF);
1885130561Sobrien		return (error);
1886130561Sobrien	}
1887130561Sobrien	cm->cm_flags = MFI_CMD_DATAIN;
1888130561Sobrien	dcmd = &cm->cm_frame->dcmd;
1889130561Sobrien	dcmd->mbox[0] = id;
1890130561Sobrien	if (mfi_wait_command(sc, cm) != 0) {
1891130561Sobrien		device_printf(sc->mfi_dev,
1892130561Sobrien		    "Failed to get logical drive: %d\n", id);
1893130561Sobrien		free(ld_info, M_MFIBUF);
1894130561Sobrien		return (0);
1895130561Sobrien	}
1896130561Sobrien	if (ld_info->ld_config.params.isSSCD != 1)
1897130561Sobrien		mfi_add_ld_complete(cm);
1898130561Sobrien	else {
1899130561Sobrien		mfi_release_command(cm);
1900130561Sobrien		if (ld_info)		/* SSCD drives ld_info free here */
1901130561Sobrien			free(ld_info, M_MFIBUF);
1902130561Sobrien	}
1903130561Sobrien	return (0);
1904130561Sobrien}
1905130561Sobrien
1906130561Sobrienstatic void
1907130561Sobrienmfi_add_ld_complete(struct mfi_command *cm)
1908130561Sobrien{
1909130561Sobrien	struct mfi_frame_header *hdr;
1910130561Sobrien	struct mfi_ld_info *ld_info;
1911130561Sobrien	struct mfi_softc *sc;
1912130561Sobrien	device_t child;
1913130561Sobrien
1914130561Sobrien	sc = cm->cm_sc;
1915130561Sobrien	hdr = &cm->cm_frame->header;
1916130561Sobrien	ld_info = cm->cm_private;
1917130561Sobrien
1918130561Sobrien	if (sc->cm_map_abort || hdr->cmd_status != MFI_STAT_OK) {
1919130561Sobrien		free(ld_info, M_MFIBUF);
1920130561Sobrien		wakeup(&sc->mfi_map_sync_cm);
1921130561Sobrien		mfi_release_command(cm);
1922130561Sobrien		return;
1923130561Sobrien	}
1924130561Sobrien	wakeup(&sc->mfi_map_sync_cm);
1925130561Sobrien	mfi_release_command(cm);
1926130561Sobrien
1927130561Sobrien	mtx_unlock(&sc->mfi_io_lock);
1928130561Sobrien	mtx_lock(&Giant);
1929130561Sobrien	if ((child = device_add_child(sc->mfi_dev, "mfid", -1)) == NULL) {
1930130561Sobrien		device_printf(sc->mfi_dev, "Failed to add logical disk\n");
1931130561Sobrien		free(ld_info, M_MFIBUF);
1932130561Sobrien		mtx_unlock(&Giant);
1933130561Sobrien		mtx_lock(&sc->mfi_io_lock);
1934130561Sobrien		return;
1935130561Sobrien	}
1936130561Sobrien
1937130561Sobrien	device_set_ivars(child, ld_info);
1938130561Sobrien	device_set_desc(child, "MFI Logical Disk");
1939130561Sobrien	bus_generic_attach(sc->mfi_dev);
1940130561Sobrien	mtx_unlock(&Giant);
1941130561Sobrien	mtx_lock(&sc->mfi_io_lock);
1942130561Sobrien}
1943130561Sobrien
194459024Sobrienstatic int mfi_add_sys_pd(struct mfi_softc *sc, int id)
1945130561Sobrien{
1946130561Sobrien	struct mfi_command *cm;
194759024Sobrien	struct mfi_dcmd_frame *dcmd = NULL;
194859024Sobrien	struct mfi_pd_info *pd_info = NULL;
194959024Sobrien	struct mfi_system_pending *syspd_pend;
195059024Sobrien	int error;
195159024Sobrien
195259024Sobrien	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
195359024Sobrien
195459024Sobrien	syspd_pend = malloc(sizeof(*syspd_pend), M_MFIBUF, M_NOWAIT | M_ZERO);
195559024Sobrien	if (syspd_pend != NULL) {
195659024Sobrien		syspd_pend->pd_id = id;
195759024Sobrien		TAILQ_INSERT_TAIL(&sc->mfi_syspd_pend_tqh, syspd_pend, pd_link);
195859024Sobrien	}
195959024Sobrien
196059024Sobrien	error = mfi_dcmd_command(sc, &cm, MFI_DCMD_PD_GET_INFO,
196177298Sobrien		(void **)&pd_info, sizeof(*pd_info));
196259024Sobrien	if (error) {
196359024Sobrien		device_printf(sc->mfi_dev,
196459024Sobrien		    "Failed to allocated for MFI_DCMD_PD_GET_INFO %d\n",
196559024Sobrien		    error);
196659024Sobrien		if (pd_info)
196759024Sobrien			free(pd_info, M_MFIBUF);
196859024Sobrien		return (error);
196977298Sobrien	}
197089857Sobrien	cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_POLLED;
197159024Sobrien	dcmd = &cm->cm_frame->dcmd;
197289857Sobrien	dcmd->mbox[0]=id;
197359024Sobrien	dcmd->header.scsi_status = 0;
197459024Sobrien	dcmd->header.pad0 = 0;
197559024Sobrien	if ((error = mfi_mapcmd(sc, cm)) != 0) {
197659024Sobrien		device_printf(sc->mfi_dev,
197759024Sobrien		    "Failed to get physical drive info %d\n", id);
197859024Sobrien		free(pd_info, M_MFIBUF);
197959024Sobrien		mfi_release_command(cm);
198077298Sobrien		return (error);
198159024Sobrien	}
198259024Sobrien	bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap,
198359024Sobrien	    BUS_DMASYNC_POSTREAD);
198459024Sobrien	bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap);
198559024Sobrien	mfi_add_sys_pd_complete(cm);
198659024Sobrien	return (0);
198759024Sobrien}
198877298Sobrien
198959024Sobrienstatic void
199059024Sobrienmfi_add_sys_pd_complete(struct mfi_command *cm)
199189857Sobrien{
199259024Sobrien	struct mfi_frame_header *hdr;
199359024Sobrien	struct mfi_pd_info *pd_info;
199489857Sobrien	struct mfi_softc *sc;
199559024Sobrien	device_t child;
199659024Sobrien
199759024Sobrien	sc = cm->cm_sc;
199859024Sobrien	hdr = &cm->cm_frame->header;
199959024Sobrien	pd_info = cm->cm_private;
200059024Sobrien
200159024Sobrien	if (hdr->cmd_status != MFI_STAT_OK) {
200259024Sobrien		free(pd_info, M_MFIBUF);
200359024Sobrien		mfi_release_command(cm);
200459024Sobrien		return;
200559024Sobrien	}
200659024Sobrien	if (pd_info->fw_state != MFI_PD_STATE_SYSTEM) {
200759024Sobrien		device_printf(sc->mfi_dev, "PD=%x is not SYSTEM PD\n",
200859024Sobrien		    pd_info->ref.v.device_id);
200959024Sobrien		free(pd_info, M_MFIBUF);
201059024Sobrien		mfi_release_command(cm);
201159024Sobrien		return;
201259024Sobrien	}
201359024Sobrien	mfi_release_command(cm);
201459024Sobrien
201559024Sobrien	mtx_unlock(&sc->mfi_io_lock);
201659024Sobrien	mtx_lock(&Giant);
201759024Sobrien	if ((child = device_add_child(sc->mfi_dev, "mfisyspd", -1)) == NULL) {
201859024Sobrien		device_printf(sc->mfi_dev, "Failed to add system pd\n");
201959024Sobrien		free(pd_info, M_MFIBUF);
202059024Sobrien		mtx_unlock(&Giant);
202159024Sobrien		mtx_lock(&sc->mfi_io_lock);
202259024Sobrien		return;
202359024Sobrien	}
202459024Sobrien
202559024Sobrien	device_set_ivars(child, pd_info);
202659024Sobrien	device_set_desc(child, "MFI System PD");
202759024Sobrien	bus_generic_attach(sc->mfi_dev);
202859024Sobrien	mtx_unlock(&Giant);
202959024Sobrien	mtx_lock(&sc->mfi_io_lock);
203059024Sobrien}
203159024Sobrien
203259024Sobrienstatic struct mfi_command *
203359024Sobrienmfi_bio_command(struct mfi_softc *sc)
203459024Sobrien{
203559024Sobrien	struct bio *bio;
203659024Sobrien	struct mfi_command *cm = NULL;
203759024Sobrien
203859024Sobrien	/*reserving two commands to avoid starvation for IOCTL*/
203959024Sobrien	if (sc->mfi_qstat[MFIQ_FREE].q_length < 2) {
204059024Sobrien		return (NULL);
204159024Sobrien	}
204259024Sobrien	if ((bio = mfi_dequeue_bio(sc)) == NULL) {
204360484Sobrien		return (NULL);
204460484Sobrien	}
204559024Sobrien	if ((uintptr_t)bio->bio_driver2 == MFI_LD_IO) {
204659024Sobrien		cm = mfi_build_ldio(sc, bio);
204759024Sobrien	} else if ((uintptr_t) bio->bio_driver2 == MFI_SYS_PD_IO) {
204859024Sobrien		cm = mfi_build_syspdio(sc, bio);
204959024Sobrien	}
205059024Sobrien	if (!cm)
205159024Sobrien	    mfi_enqueue_bio(sc, bio);
205260484Sobrien	return cm;
205360484Sobrien}
205459024Sobrien
205559024Sobrien/*
205659024Sobrien * mostly copied from cam/scsi/scsi_all.c:scsi_read_write
205759024Sobrien */
205859024Sobrien
205959024Sobrienint
206059024Sobrienmfi_build_cdb(int readop, uint8_t byte2, u_int64_t lba, u_int32_t block_count, uint8_t *cdb)
206160484Sobrien{
206260484Sobrien	int cdb_len;
206359024Sobrien
206459024Sobrien	if (((lba & 0x1fffff) == lba)
206559024Sobrien         && ((block_count & 0xff) == block_count)
206659024Sobrien         && (byte2 == 0)) {
206759024Sobrien		/* We can fit in a 6 byte cdb */
206859024Sobrien		struct scsi_rw_6 *scsi_cmd;
206959024Sobrien
207060484Sobrien		scsi_cmd = (struct scsi_rw_6 *)cdb;
207160484Sobrien		scsi_cmd->opcode = readop ? READ_6 : WRITE_6;
207259024Sobrien		scsi_ulto3b(lba, scsi_cmd->addr);
207359024Sobrien		scsi_cmd->length = block_count & 0xff;
207459024Sobrien		scsi_cmd->control = 0;
207559024Sobrien		cdb_len = sizeof(*scsi_cmd);
207659024Sobrien	} else if (((block_count & 0xffff) == block_count) && ((lba & 0xffffffff) == lba)) {
207759024Sobrien		/* Need a 10 byte CDB */
207859024Sobrien		struct scsi_rw_10 *scsi_cmd;
207959024Sobrien
208059024Sobrien		scsi_cmd = (struct scsi_rw_10 *)cdb;
208159024Sobrien		scsi_cmd->opcode = readop ? READ_10 : WRITE_10;
208259024Sobrien		scsi_cmd->byte2 = byte2;
208359024Sobrien		scsi_ulto4b(lba, scsi_cmd->addr);
208459024Sobrien		scsi_cmd->reserved = 0;
208559024Sobrien		scsi_ulto2b(block_count, scsi_cmd->length);
208659024Sobrien		scsi_cmd->control = 0;
208789857Sobrien		cdb_len = sizeof(*scsi_cmd);
208859024Sobrien	} else if (((block_count & 0xffffffff) == block_count) &&
208959024Sobrien	    ((lba & 0xffffffff) == lba)) {
209059024Sobrien		/* Block count is too big for 10 byte CDB use a 12 byte CDB */
209159024Sobrien		struct scsi_rw_12 *scsi_cmd;
209259024Sobrien
209359024Sobrien		scsi_cmd = (struct scsi_rw_12 *)cdb;
209459024Sobrien		scsi_cmd->opcode = readop ? READ_12 : WRITE_12;
209559024Sobrien		scsi_cmd->byte2 = byte2;
209659024Sobrien		scsi_ulto4b(lba, scsi_cmd->addr);
209759024Sobrien		scsi_cmd->reserved = 0;
209859024Sobrien		scsi_ulto4b(block_count, scsi_cmd->length);
209959024Sobrien		scsi_cmd->control = 0;
210059024Sobrien		cdb_len = sizeof(*scsi_cmd);
210159024Sobrien	} else {
210289857Sobrien		/*
210359024Sobrien		 * 16 byte CDB.  We'll only get here if the LBA is larger
210459024Sobrien		 * than 2^32
210559024Sobrien		 */
210659024Sobrien		struct scsi_rw_16 *scsi_cmd;
210759024Sobrien
210859024Sobrien		scsi_cmd = (struct scsi_rw_16 *)cdb;
210959024Sobrien		scsi_cmd->opcode = readop ? READ_16 : WRITE_16;
211059024Sobrien		scsi_cmd->byte2 = byte2;
211159024Sobrien		scsi_u64to8b(lba, scsi_cmd->addr);
211259024Sobrien		scsi_cmd->reserved = 0;
211359024Sobrien		scsi_ulto4b(block_count, scsi_cmd->length);
211459024Sobrien		scsi_cmd->control = 0;
211559024Sobrien		cdb_len = sizeof(*scsi_cmd);
211659024Sobrien	}
211759024Sobrien
211859024Sobrien	return cdb_len;
211959024Sobrien}
212060484Sobrien
212177298Sobrienextern char *unmapped_buf;
212260484Sobrien
212360484Sobrienstatic struct mfi_command *
212459024Sobrienmfi_build_syspdio(struct mfi_softc *sc, struct bio *bio)
212559024Sobrien{
212659024Sobrien	struct mfi_command *cm;
212759024Sobrien	struct mfi_pass_frame *pass;
212859024Sobrien	uint32_t context = 0;
212959024Sobrien	int flags = 0, blkcount = 0, readop;
213059024Sobrien	uint8_t cdb_len;
213159024Sobrien
213259024Sobrien	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
213359024Sobrien
213459024Sobrien	if ((cm = mfi_dequeue_free(sc)) == NULL)
213559024Sobrien	    return (NULL);
213659024Sobrien
213759024Sobrien	/* Zero out the MFI frame */
213859024Sobrien	context = cm->cm_frame->header.context;
213959024Sobrien	bzero(cm->cm_frame, sizeof(union mfi_frame));
214059024Sobrien	cm->cm_frame->header.context = context;
214159024Sobrien	pass = &cm->cm_frame->pass;
214259024Sobrien	bzero(pass->cdb, 16);
214359024Sobrien	pass->header.cmd = MFI_CMD_PD_SCSI_IO;
214459024Sobrien	switch (bio->bio_cmd) {
214559024Sobrien	case BIO_READ:
214659024Sobrien		flags = MFI_CMD_DATAIN | MFI_CMD_BIO;
214759024Sobrien		readop = 1;
214859024Sobrien		break;
214959024Sobrien	case BIO_WRITE:
215059024Sobrien		flags = MFI_CMD_DATAOUT | MFI_CMD_BIO;
215159024Sobrien		readop = 0;
215259024Sobrien		break;
215359024Sobrien	default:
215459024Sobrien		/* TODO: what about BIO_DELETE??? */
215559024Sobrien		panic("Unsupported bio command %x\n", bio->bio_cmd);
215659024Sobrien	}
215759024Sobrien
215859024Sobrien	/* Cheat with the sector length to avoid a non-constant division */
215959024Sobrien	blkcount = howmany(bio->bio_bcount, MFI_SECTOR_LEN);
216059024Sobrien	/* Fill the LBA and Transfer length in CDB */
216159024Sobrien	cdb_len = mfi_build_cdb(readop, 0, bio->bio_pblkno, blkcount,
216259024Sobrien	    pass->cdb);
216359024Sobrien	pass->header.target_id = (uintptr_t)bio->bio_driver1;
216459024Sobrien	pass->header.lun_id = 0;
216559024Sobrien	pass->header.timeout = 0;
216659024Sobrien	pass->header.flags = 0;
216789857Sobrien	pass->header.scsi_status = 0;
216859024Sobrien	pass->header.sense_len = MFI_SENSE_LEN;
216989857Sobrien	pass->header.data_len = bio->bio_bcount;
217059024Sobrien	pass->header.cdb_len = cdb_len;
217159024Sobrien	pass->sense_addr_lo = (uint32_t)cm->cm_sense_busaddr;
217259024Sobrien	pass->sense_addr_hi = (uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32);
217359024Sobrien	cm->cm_complete = mfi_bio_complete;
217459024Sobrien	cm->cm_private = bio;
217559024Sobrien	cm->cm_data = unmapped_buf;
217659024Sobrien	cm->cm_len = bio->bio_bcount;
217759024Sobrien	cm->cm_sg = &pass->sgl;
217859024Sobrien	cm->cm_total_frame_size = MFI_PASS_FRAME_SIZE;
217959024Sobrien	cm->cm_flags = flags;
218059024Sobrien
218159024Sobrien	return (cm);
218259024Sobrien}
218359024Sobrien
218459024Sobrienstatic struct mfi_command *
218559024Sobrienmfi_build_ldio(struct mfi_softc *sc, struct bio *bio)
218659024Sobrien{
218759024Sobrien	struct mfi_io_frame *io;
218859024Sobrien	struct mfi_command *cm;
218959024Sobrien	int flags;
219059024Sobrien	uint32_t blkcount;
219159024Sobrien	uint32_t context = 0;
219259024Sobrien
219360484Sobrien	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
219459024Sobrien
219560484Sobrien	if ((cm = mfi_dequeue_free(sc)) == NULL)
219659024Sobrien	    return (NULL);
219759024Sobrien
219859024Sobrien	/* Zero out the MFI frame */
219959024Sobrien	context = cm->cm_frame->header.context;
220059024Sobrien	bzero(cm->cm_frame, sizeof(union mfi_frame));
220159024Sobrien	cm->cm_frame->header.context = context;
2202130561Sobrien	io = &cm->cm_frame->io;
2203130561Sobrien	switch (bio->bio_cmd) {
2204130561Sobrien	case BIO_READ:
2205130561Sobrien		io->header.cmd = MFI_CMD_LD_READ;
2206130561Sobrien		flags = MFI_CMD_DATAIN | MFI_CMD_BIO;
2207130561Sobrien		break;
220859024Sobrien	case BIO_WRITE:
220959024Sobrien		io->header.cmd = MFI_CMD_LD_WRITE;
221059024Sobrien		flags = MFI_CMD_DATAOUT | MFI_CMD_BIO;
221159024Sobrien		break;
221259024Sobrien	default:
221360484Sobrien		/* TODO: what about BIO_DELETE??? */
221459024Sobrien		panic("Unsupported bio command %x\n", bio->bio_cmd);
221559024Sobrien	}
221659024Sobrien
221759024Sobrien	/* Cheat with the sector length to avoid a non-constant division */
221859024Sobrien	blkcount = howmany(bio->bio_bcount, MFI_SECTOR_LEN);
221959024Sobrien	io->header.target_id = (uintptr_t)bio->bio_driver1;
222059024Sobrien	io->header.timeout = 0;
222177298Sobrien	io->header.flags = 0;
222259024Sobrien	io->header.scsi_status = 0;
222359024Sobrien	io->header.sense_len = MFI_SENSE_LEN;
222459024Sobrien	io->header.data_len = blkcount;
222559024Sobrien	io->sense_addr_lo = (uint32_t)cm->cm_sense_busaddr;
222659024Sobrien	io->sense_addr_hi = (uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32);
222759024Sobrien	io->lba_hi = (bio->bio_pblkno & 0xffffffff00000000) >> 32;
222859024Sobrien	io->lba_lo = bio->bio_pblkno & 0xffffffff;
222959024Sobrien	cm->cm_complete = mfi_bio_complete;
223059024Sobrien	cm->cm_private = bio;
223159024Sobrien	cm->cm_data = unmapped_buf;
223259024Sobrien	cm->cm_len = bio->bio_bcount;
223359024Sobrien	cm->cm_sg = &io->sgl;
223459024Sobrien	cm->cm_total_frame_size = MFI_IO_FRAME_SIZE;
223559024Sobrien	cm->cm_flags = flags;
223659024Sobrien
223759024Sobrien	return (cm);
223859024Sobrien}
223959024Sobrien
224059024Sobrienstatic void
224159024Sobrienmfi_bio_complete(struct mfi_command *cm)
224277298Sobrien{
224359024Sobrien	struct bio *bio;
224459024Sobrien	struct mfi_frame_header *hdr;
224559024Sobrien	struct mfi_softc *sc;
224677298Sobrien
224759024Sobrien	bio = cm->cm_private;
224859024Sobrien	hdr = &cm->cm_frame->header;
224959024Sobrien	sc = cm->cm_sc;
225059024Sobrien
225159024Sobrien	if ((hdr->cmd_status != MFI_STAT_OK) || (hdr->scsi_status != 0)) {
225259024Sobrien		bio->bio_flags |= BIO_ERROR;
225359024Sobrien		bio->bio_error = EIO;
225459024Sobrien		device_printf(sc->mfi_dev, "I/O error, cmd=%p, status=%#x, "
225559024Sobrien		    "scsi_status=%#x\n", cm, hdr->cmd_status, hdr->scsi_status);
225677298Sobrien		mfi_print_sense(cm->cm_sc, cm->cm_sense);
225777298Sobrien	} else if (cm->cm_error != 0) {
225859024Sobrien		bio->bio_flags |= BIO_ERROR;
225959024Sobrien		bio->bio_error = cm->cm_error;
226077298Sobrien		device_printf(sc->mfi_dev, "I/O error, cmd=%p, error=%#x\n",
226159024Sobrien		    cm, cm->cm_error);
226259024Sobrien	}
226359024Sobrien
226459024Sobrien	mfi_release_command(cm);
226577298Sobrien	mfi_disk_complete(bio);
226659024Sobrien}
226759024Sobrien
226859024Sobrienvoid
226959024Sobrienmfi_startio(struct mfi_softc *sc)
227060484Sobrien{
227177298Sobrien	struct mfi_command *cm;
227259024Sobrien	struct ccb_hdr *ccbh;
227359024Sobrien
227459024Sobrien	for (;;) {
227577298Sobrien		/* Don't bother if we're short on resources */
227659024Sobrien		if (sc->mfi_flags & MFI_FLAGS_QFRZN)
227759024Sobrien			break;
227859024Sobrien
227959024Sobrien		/* Try a command that has already been prepared */
228059024Sobrien		cm = mfi_dequeue_ready(sc);
228159024Sobrien
228259024Sobrien		if (cm == NULL) {
228359024Sobrien			if ((ccbh = TAILQ_FIRST(&sc->mfi_cam_ccbq)) != NULL)
228460484Sobrien				cm = sc->mfi_cam_start(ccbh);
228560484Sobrien		}
228660484Sobrien
2287218822Sdim		/* Nope, so look for work on the bioq */
228860484Sobrien		if (cm == NULL)
228959024Sobrien			cm = mfi_bio_command(sc);
229060484Sobrien
229160484Sobrien		/* No work available, so exit */
229260484Sobrien		if (cm == NULL)
229360484Sobrien			break;
229460484Sobrien
229560484Sobrien		/* Send the command to the controller */
229660484Sobrien		if (mfi_mapcmd(sc, cm) != 0) {
229760484Sobrien			device_printf(sc->mfi_dev, "Failed to startio\n");
229860484Sobrien			mfi_requeue_ready(cm);
229960484Sobrien			break;
230060484Sobrien		}
230160484Sobrien	}
230260484Sobrien}
230360484Sobrien
230460484Sobrienint
230560484Sobrienmfi_mapcmd(struct mfi_softc *sc, struct mfi_command *cm)
230660484Sobrien{
230760484Sobrien	int error, polled;
230860484Sobrien
230960484Sobrien	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
231060484Sobrien
231160484Sobrien	if ((cm->cm_data != NULL) && (cm->cm_frame->header.cmd != MFI_CMD_STP )) {
231260484Sobrien		polled = (cm->cm_flags & MFI_CMD_POLLED) ? BUS_DMA_NOWAIT : 0;
231360484Sobrien		if (cm->cm_flags & MFI_CMD_CCB)
231460484Sobrien			error = bus_dmamap_load_ccb(sc->mfi_buffer_dmat,
231560484Sobrien			    cm->cm_dmamap, cm->cm_data, mfi_data_cb, cm,
231660484Sobrien			    polled);
231760484Sobrien		else if (cm->cm_flags & MFI_CMD_BIO)
231860484Sobrien			error = bus_dmamap_load_bio(sc->mfi_buffer_dmat,
2319130561Sobrien			    cm->cm_dmamap, cm->cm_private, mfi_data_cb, cm,
2320130561Sobrien			    polled);
2321130561Sobrien		else
2322130561Sobrien			error = bus_dmamap_load(sc->mfi_buffer_dmat,
2323130561Sobrien			    cm->cm_dmamap, cm->cm_data, cm->cm_len,
2324130561Sobrien			    mfi_data_cb, cm, polled);
2325130561Sobrien		if (error == EINPROGRESS) {
2326130561Sobrien			sc->mfi_flags |= MFI_FLAGS_QFRZN;
2327130561Sobrien			return (0);
2328130561Sobrien		}
2329130561Sobrien	} else {
2330130561Sobrien		error = mfi_send_frame(sc, cm);
233177298Sobrien	}
233260484Sobrien
233360484Sobrien	return (error);
233477298Sobrien}
233560484Sobrien
233660484Sobrienstatic void
233760484Sobrienmfi_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
233860484Sobrien{
233959024Sobrien	struct mfi_frame_header *hdr;
234077298Sobrien	struct mfi_command *cm;
234160484Sobrien	union mfi_sgl *sgl;
234260484Sobrien	struct mfi_softc *sc;
234360484Sobrien	int i, j, first, dir;
234460484Sobrien	int sge_size, locked;
234560484Sobrien
234659024Sobrien	cm = (struct mfi_command *)arg;
234760484Sobrien	sc = cm->cm_sc;
234860484Sobrien	hdr = &cm->cm_frame->header;
234960484Sobrien	sgl = cm->cm_sg;
235060484Sobrien
235160484Sobrien	/*
235259024Sobrien	 * We need to check if we have the lock as this is async
235360484Sobrien	 * callback so even though our caller mfi_mapcmd asserts
235460484Sobrien	 * it has the lock, there is no guarantee that hasn't been
235560484Sobrien	 * dropped if bus_dmamap_load returned prior to our
235660484Sobrien	 * completion.
235760484Sobrien	 */
235859024Sobrien	if ((locked = mtx_owned(&sc->mfi_io_lock)) == 0)
235959024Sobrien		mtx_lock(&sc->mfi_io_lock);
236059024Sobrien
236159024Sobrien	if (error) {
236259024Sobrien		printf("error %d in callback\n", error);
236360484Sobrien		cm->cm_error = error;
236460484Sobrien		mfi_complete(sc, cm);
236560484Sobrien		goto out;
236659024Sobrien	}
236760484Sobrien	/* Use IEEE sgl only for IO's on a SKINNY controller
236860484Sobrien	 * For other commands on a SKINNY controller use either
236960484Sobrien	 * sg32 or sg64 based on the sizeof(bus_addr_t).
237060484Sobrien	 * Also calculate the total frame size based on the type
237160484Sobrien	 * of SGL used.
237260484Sobrien	 */
237360484Sobrien	if (((cm->cm_frame->header.cmd == MFI_CMD_PD_SCSI_IO) ||
237460484Sobrien	    (cm->cm_frame->header.cmd == MFI_CMD_LD_READ) ||
237560484Sobrien	    (cm->cm_frame->header.cmd == MFI_CMD_LD_WRITE)) &&
237660484Sobrien	    (sc->mfi_flags & MFI_FLAGS_SKINNY)) {
237760484Sobrien		for (i = 0; i < nsegs; i++) {
237860484Sobrien			sgl->sg_skinny[i].addr = segs[i].ds_addr;
237960484Sobrien			sgl->sg_skinny[i].len = segs[i].ds_len;
238060484Sobrien			sgl->sg_skinny[i].flag = 0;
238160484Sobrien		}
238277298Sobrien		hdr->flags |= MFI_FRAME_IEEE_SGL | MFI_FRAME_SGL64;
238360484Sobrien		sge_size = sizeof(struct mfi_sg_skinny);
238460484Sobrien		hdr->sg_count = nsegs;
238560484Sobrien	} else {
238660484Sobrien		j = 0;
238760484Sobrien		if (cm->cm_frame->header.cmd == MFI_CMD_STP) {
238860484Sobrien			first = cm->cm_stp_len;
238960484Sobrien			if ((sc->mfi_flags & MFI_FLAGS_SG64) == 0) {
239060484Sobrien				sgl->sg32[j].addr = segs[0].ds_addr;
239160484Sobrien				sgl->sg32[j++].len = first;
239260484Sobrien			} else {
239360484Sobrien				sgl->sg64[j].addr = segs[0].ds_addr;
239460484Sobrien				sgl->sg64[j++].len = first;
239560484Sobrien			}
239660484Sobrien		} else
239777298Sobrien			first = 0;
239860484Sobrien		if ((sc->mfi_flags & MFI_FLAGS_SG64) == 0) {
239960484Sobrien			for (i = 0; i < nsegs; i++) {
240077298Sobrien				sgl->sg32[j].addr = segs[i].ds_addr + first;
240177298Sobrien				sgl->sg32[j++].len = segs[i].ds_len - first;
240259024Sobrien				first = 0;
240389857Sobrien			}
240459024Sobrien		} else {
240559024Sobrien			for (i = 0; i < nsegs; i++) {
240660484Sobrien				sgl->sg64[j].addr = segs[i].ds_addr + first;
240760484Sobrien				sgl->sg64[j++].len = segs[i].ds_len - first;
240860484Sobrien				first = 0;
240960484Sobrien			}
241060484Sobrien			hdr->flags |= MFI_FRAME_SGL64;
241160484Sobrien		}
241259024Sobrien		hdr->sg_count = j;
241359024Sobrien		sge_size = sc->mfi_sge_size;
241460484Sobrien	}
241560484Sobrien
241660484Sobrien	dir = 0;
241760484Sobrien	if (cm->cm_flags & MFI_CMD_DATAIN) {
241859024Sobrien		dir |= BUS_DMASYNC_PREREAD;
241960484Sobrien		hdr->flags |= MFI_FRAME_DIR_READ;
242060484Sobrien	}
242159024Sobrien	if (cm->cm_flags & MFI_CMD_DATAOUT) {
242259024Sobrien		dir |= BUS_DMASYNC_PREWRITE;
242360484Sobrien		hdr->flags |= MFI_FRAME_DIR_WRITE;
242460484Sobrien	}
242560484Sobrien	bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap, dir);
242660484Sobrien	cm->cm_flags |= MFI_CMD_MAPPED;
242760484Sobrien
242860484Sobrien	/*
242960484Sobrien	 * Instead of calculating the total number of frames in the
243060484Sobrien	 * compound frame, it's already assumed that there will be at
243160484Sobrien	 * least 1 frame, so don't compensate for the modulo of the
243260484Sobrien	 * following division.
243360484Sobrien	 */
243460484Sobrien	cm->cm_total_frame_size += (sc->mfi_sge_size * nsegs);
243560484Sobrien	cm->cm_extra_frames = (cm->cm_total_frame_size - 1) / MFI_FRAME_SIZE;
243660484Sobrien
243760484Sobrien	if ((error = mfi_send_frame(sc, cm)) != 0) {
243860484Sobrien		printf("error %d in callback from mfi_send_frame\n", error);
243960484Sobrien		cm->cm_error = error;
244060484Sobrien		mfi_complete(sc, cm);
244160484Sobrien		goto out;
244259024Sobrien	}
244360484Sobrien
244460484Sobrienout:
244559024Sobrien	/* leave the lock in the state we found it */
244660484Sobrien	if (locked == 0)
244760484Sobrien		mtx_unlock(&sc->mfi_io_lock);
244860484Sobrien
244960484Sobrien	return;
245060484Sobrien}
245160484Sobrien
245260484Sobrienstatic int
245360484Sobrienmfi_send_frame(struct mfi_softc *sc, struct mfi_command *cm)
245460484Sobrien{
245560484Sobrien	int error;
245677298Sobrien
245760484Sobrien	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
245860484Sobrien
245960484Sobrien	if (sc->MFA_enabled)
246060484Sobrien		error = mfi_tbolt_send_frame(sc, cm);
246160484Sobrien	else
246260484Sobrien		error = mfi_std_send_frame(sc, cm);
246360484Sobrien
246460484Sobrien	if (error != 0 && (cm->cm_flags & MFI_ON_MFIQ_BUSY) != 0)
246577298Sobrien		mfi_remove_busy(cm);
246660484Sobrien
246760484Sobrien	return (error);
246860484Sobrien}
246960484Sobrien
247060484Sobrienstatic int
247160484Sobrienmfi_std_send_frame(struct mfi_softc *sc, struct mfi_command *cm)
247260484Sobrien{
247360484Sobrien	struct mfi_frame_header *hdr;
247460484Sobrien	int tm = mfi_polled_cmd_timeout * 1000;
247560484Sobrien
247660484Sobrien	hdr = &cm->cm_frame->header;
247760484Sobrien
247860484Sobrien	if ((cm->cm_flags & MFI_CMD_POLLED) == 0) {
247960484Sobrien		cm->cm_timestamp = time_uptime;
248060484Sobrien		mfi_enqueue_busy(cm);
248160484Sobrien	} else {
248260484Sobrien		hdr->cmd_status = MFI_STAT_INVALID_STATUS;
248360484Sobrien		hdr->flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE;
248460484Sobrien	}
248560484Sobrien
248677298Sobrien	/*
248760484Sobrien	 * The bus address of the command is aligned on a 64 byte boundary,
248860484Sobrien	 * leaving the least 6 bits as zero.  For whatever reason, the
248960484Sobrien	 * hardware wants the address shifted right by three, leaving just
249060484Sobrien	 * 3 zero bits.  These three bits are then used as a prefetching
249160484Sobrien	 * hint for the hardware to predict how many frames need to be
249260484Sobrien	 * fetched across the bus.  If a command has more than 8 frames
249360484Sobrien	 * then the 3 bits are set to 0x7 and the firmware uses other
249460484Sobrien	 * information in the command to determine the total amount to fetch.
249560484Sobrien	 * However, FreeBSD doesn't support I/O larger than 128K, so 8 frames
249660484Sobrien	 * is enough for both 32bit and 64bit systems.
249760484Sobrien	 */
249860484Sobrien	if (cm->cm_extra_frames > 7)
249960484Sobrien		cm->cm_extra_frames = 7;
250060484Sobrien
250160484Sobrien	sc->mfi_issue_cmd(sc, cm->cm_frame_busaddr, cm->cm_extra_frames);
250260484Sobrien
250360484Sobrien	if ((cm->cm_flags & MFI_CMD_POLLED) == 0)
250460484Sobrien		return (0);
250560484Sobrien
250660484Sobrien	/* This is a polled command, so busy-wait for it to complete. */
250760484Sobrien	while (hdr->cmd_status == MFI_STAT_INVALID_STATUS) {
250860484Sobrien		DELAY(1000);
250960484Sobrien		tm -= 1;
251060484Sobrien		if (tm <= 0)
251160484Sobrien			break;
251260484Sobrien	}
251360484Sobrien
251460484Sobrien	if (hdr->cmd_status == MFI_STAT_INVALID_STATUS) {
251560484Sobrien		device_printf(sc->mfi_dev, "Frame %p timed out "
251659024Sobrien		    "command 0x%X\n", hdr, cm->cm_frame->dcmd.opcode);
251759024Sobrien		return (ETIMEDOUT);
251859024Sobrien	}
251959024Sobrien
252059024Sobrien	return (0);
252159024Sobrien}
252259024Sobrien
252359024Sobrien
252459024Sobrienvoid
252559024Sobrienmfi_complete(struct mfi_softc *sc, struct mfi_command *cm)
252659024Sobrien{
252759024Sobrien	int dir;
252859024Sobrien	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
252960484Sobrien
253059024Sobrien	if ((cm->cm_flags & MFI_CMD_MAPPED) != 0) {
253159024Sobrien		dir = 0;
253259024Sobrien		if ((cm->cm_flags & MFI_CMD_DATAIN) ||
2533130561Sobrien		    (cm->cm_frame->header.cmd == MFI_CMD_STP))
2534130561Sobrien			dir |= BUS_DMASYNC_POSTREAD;
2535130561Sobrien		if (cm->cm_flags & MFI_CMD_DATAOUT)
2536130561Sobrien			dir |= BUS_DMASYNC_POSTWRITE;
2537130561Sobrien
2538130561Sobrien		bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap, dir);
2539130561Sobrien		bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap);
2540218822Sdim		cm->cm_flags &= ~MFI_CMD_MAPPED;
254159024Sobrien	}
254259024Sobrien
254359024Sobrien	cm->cm_flags |= MFI_CMD_COMPLETED;
254459024Sobrien
2545218822Sdim	if (cm->cm_complete != NULL)
254659024Sobrien		cm->cm_complete(cm);
254759024Sobrien	else
254859024Sobrien		wakeup(cm);
254959024Sobrien}
255059024Sobrien
255159024Sobrienstatic int
255259024Sobrienmfi_abort(struct mfi_softc *sc, struct mfi_command **cm_abort)
255359024Sobrien{
255459024Sobrien	struct mfi_command *cm;
255559024Sobrien	struct mfi_abort_frame *abort;
255659024Sobrien	int i = 0, error;
255759024Sobrien	uint32_t context = 0;
255859024Sobrien
255959024Sobrien	mtx_lock(&sc->mfi_io_lock);
256059024Sobrien	if ((cm = mfi_dequeue_free(sc)) == NULL) {
256159024Sobrien		mtx_unlock(&sc->mfi_io_lock);
256259024Sobrien		return (EBUSY);
256359024Sobrien	}
256459024Sobrien
256559024Sobrien	/* Zero out the MFI frame */
256659024Sobrien	context = cm->cm_frame->header.context;
256759024Sobrien	bzero(cm->cm_frame, sizeof(union mfi_frame));
256859024Sobrien	cm->cm_frame->header.context = context;
256960484Sobrien
257059024Sobrien	abort = &cm->cm_frame->abort;
257159024Sobrien	abort->header.cmd = MFI_CMD_ABORT;
257259024Sobrien	abort->header.flags = 0;
257359024Sobrien	abort->header.scsi_status = 0;
257459024Sobrien	abort->abort_context = (*cm_abort)->cm_frame->header.context;
257559024Sobrien	abort->abort_mfi_addr_lo = (uint32_t)(*cm_abort)->cm_frame_busaddr;
257659024Sobrien	abort->abort_mfi_addr_hi =
257760484Sobrien		(uint32_t)((uint64_t)(*cm_abort)->cm_frame_busaddr >> 32);
257859024Sobrien	cm->cm_data = NULL;
257959024Sobrien	cm->cm_flags = MFI_CMD_POLLED;
258059024Sobrien
258159024Sobrien	if ((error = mfi_mapcmd(sc, cm)) != 0)
258260484Sobrien		device_printf(sc->mfi_dev, "failed to abort command\n");
258359024Sobrien	mfi_release_command(cm);
258459024Sobrien
258559024Sobrien	mtx_unlock(&sc->mfi_io_lock);
258659024Sobrien	while (i < 5 && *cm_abort != NULL) {
258759024Sobrien		tsleep(cm_abort, 0, "mfiabort",
258877298Sobrien		    5 * hz);
258959024Sobrien		i++;
259059024Sobrien	}
259159024Sobrien	if (*cm_abort != NULL) {
259259024Sobrien		/* Force a complete if command didn't abort */
259359024Sobrien		mtx_lock(&sc->mfi_io_lock);
259459024Sobrien		(*cm_abort)->cm_complete(*cm_abort);
259559024Sobrien		mtx_unlock(&sc->mfi_io_lock);
259659024Sobrien	}
259759024Sobrien
259877298Sobrien	return (error);
259959024Sobrien}
260059024Sobrien
260159024Sobrienint
260259024Sobrienmfi_dump_blocks(struct mfi_softc *sc, int id, uint64_t lba, void *virt,
260359024Sobrien     int len)
260459024Sobrien{
260559024Sobrien	struct mfi_command *cm;
260677298Sobrien	struct mfi_io_frame *io;
260759024Sobrien	int error;
260859024Sobrien	uint32_t context = 0;
260959024Sobrien
261059024Sobrien	if ((cm = mfi_dequeue_free(sc)) == NULL)
261159024Sobrien		return (EBUSY);
261259024Sobrien
261359024Sobrien	/* Zero out the MFI frame */
261459024Sobrien	context = cm->cm_frame->header.context;
261559024Sobrien	bzero(cm->cm_frame, sizeof(union mfi_frame));
261659024Sobrien	cm->cm_frame->header.context = context;
261759024Sobrien
261859024Sobrien	io = &cm->cm_frame->io;
261959024Sobrien	io->header.cmd = MFI_CMD_LD_WRITE;
262059024Sobrien	io->header.target_id = id;
262159024Sobrien	io->header.timeout = 0;
262259024Sobrien	io->header.flags = 0;
262359024Sobrien	io->header.scsi_status = 0;
262459024Sobrien	io->header.sense_len = MFI_SENSE_LEN;
262559024Sobrien	io->header.data_len = howmany(len, MFI_SECTOR_LEN);
262659024Sobrien	io->sense_addr_lo = (uint32_t)cm->cm_sense_busaddr;
262759024Sobrien	io->sense_addr_hi = (uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32);
262859024Sobrien	io->lba_hi = (lba & 0xffffffff00000000) >> 32;
262959024Sobrien	io->lba_lo = lba & 0xffffffff;
263059024Sobrien	cm->cm_data = virt;
263159024Sobrien	cm->cm_len = len;
263259024Sobrien	cm->cm_sg = &io->sgl;
263359024Sobrien	cm->cm_total_frame_size = MFI_IO_FRAME_SIZE;
263459024Sobrien	cm->cm_flags = MFI_CMD_POLLED | MFI_CMD_DATAOUT;
263559024Sobrien
263659024Sobrien	if ((error = mfi_mapcmd(sc, cm)) != 0)
263759024Sobrien		device_printf(sc->mfi_dev, "failed dump blocks\n");
263859024Sobrien	bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap,
263959024Sobrien	    BUS_DMASYNC_POSTWRITE);
264059024Sobrien	bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap);
264159024Sobrien	mfi_release_command(cm);
264259024Sobrien
264359024Sobrien	return (error);
264459024Sobrien}
264559024Sobrien
264659024Sobrienint
264759024Sobrienmfi_dump_syspd_blocks(struct mfi_softc *sc, int id, uint64_t lba, void *virt,
264859024Sobrien    int len)
264959024Sobrien{
265059024Sobrien	struct mfi_command *cm;
265159024Sobrien	struct mfi_pass_frame *pass;
265259024Sobrien	int error, readop, cdb_len;
265359024Sobrien	uint32_t blkcount;
265459024Sobrien
265559024Sobrien	if ((cm = mfi_dequeue_free(sc)) == NULL)
265659024Sobrien		return (EBUSY);
265759024Sobrien
265859024Sobrien	pass = &cm->cm_frame->pass;
265959024Sobrien	bzero(pass->cdb, 16);
266059024Sobrien	pass->header.cmd = MFI_CMD_PD_SCSI_IO;
266159024Sobrien
266259024Sobrien	readop = 0;
266359024Sobrien	blkcount = howmany(len, MFI_SECTOR_LEN);
266459024Sobrien	cdb_len = mfi_build_cdb(readop, 0, lba, blkcount, pass->cdb);
266559024Sobrien	pass->header.target_id = id;
266659024Sobrien	pass->header.timeout = 0;
266759024Sobrien	pass->header.flags = 0;
266859024Sobrien	pass->header.scsi_status = 0;
266959024Sobrien	pass->header.sense_len = MFI_SENSE_LEN;
267059024Sobrien	pass->header.data_len = len;
267159024Sobrien	pass->header.cdb_len = cdb_len;
267259024Sobrien	pass->sense_addr_lo = (uint32_t)cm->cm_sense_busaddr;
267359024Sobrien	pass->sense_addr_hi = (uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32);
267459024Sobrien	cm->cm_data = virt;
267559024Sobrien	cm->cm_len = len;
267659024Sobrien	cm->cm_sg = &pass->sgl;
267759024Sobrien	cm->cm_total_frame_size = MFI_PASS_FRAME_SIZE;
267859024Sobrien	cm->cm_flags = MFI_CMD_POLLED | MFI_CMD_DATAOUT | MFI_CMD_SCSI;
267960484Sobrien
268059024Sobrien	if ((error = mfi_mapcmd(sc, cm)) != 0)
268159024Sobrien		device_printf(sc->mfi_dev, "failed dump blocks\n");
268259024Sobrien	bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap,
268359024Sobrien	    BUS_DMASYNC_POSTWRITE);
268460484Sobrien	bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap);
268559024Sobrien	mfi_release_command(cm);
268659024Sobrien
268759024Sobrien	return (error);
268859024Sobrien}
268959024Sobrien
269059024Sobrienstatic int
269159024Sobrienmfi_open(struct cdev *dev, int flags, int fmt, struct thread *td)
269259024Sobrien{
269359024Sobrien	struct mfi_softc *sc;
269459024Sobrien	int error;
269559024Sobrien
269659024Sobrien	sc = dev->si_drv1;
269759024Sobrien
269859024Sobrien	mtx_lock(&sc->mfi_io_lock);
269959024Sobrien	if (sc->mfi_detaching)
270059024Sobrien		error = ENXIO;
270159024Sobrien	else {
270259024Sobrien		sc->mfi_flags |= MFI_FLAGS_OPEN;
270360484Sobrien		error = 0;
270459024Sobrien	}
270559024Sobrien	mtx_unlock(&sc->mfi_io_lock);
270659024Sobrien
270759024Sobrien	return (error);
270859024Sobrien}
270959024Sobrien
271059024Sobrienstatic int
271160484Sobrienmfi_close(struct cdev *dev, int flags, int fmt, struct thread *td)
271277298Sobrien{
271359024Sobrien	struct mfi_softc *sc;
271459024Sobrien	struct mfi_aen *mfi_aen_entry, *tmp;
271559024Sobrien
271677298Sobrien	sc = dev->si_drv1;
271759024Sobrien
271859024Sobrien	mtx_lock(&sc->mfi_io_lock);
271959024Sobrien	sc->mfi_flags &= ~MFI_FLAGS_OPEN;
272059024Sobrien
272177298Sobrien	TAILQ_FOREACH_SAFE(mfi_aen_entry, &sc->mfi_aen_pids, aen_link, tmp) {
272259024Sobrien		if (mfi_aen_entry->p == curproc) {
272359024Sobrien			TAILQ_REMOVE(&sc->mfi_aen_pids, mfi_aen_entry,
272459024Sobrien			    aen_link);
272559024Sobrien			free(mfi_aen_entry, M_MFIBUF);
272659024Sobrien		}
272759024Sobrien	}
272859024Sobrien	mtx_unlock(&sc->mfi_io_lock);
272959024Sobrien	return (0);
273059024Sobrien}
273159024Sobrien
273260484Sobrienstatic int
273360484Sobrienmfi_config_lock(struct mfi_softc *sc, uint32_t opcode)
273459024Sobrien{
273559024Sobrien
273659024Sobrien	switch (opcode) {
273759024Sobrien	case MFI_DCMD_LD_DELETE:
273877298Sobrien	case MFI_DCMD_CFG_ADD:
273959024Sobrien	case MFI_DCMD_CFG_CLEAR:
274059024Sobrien	case MFI_DCMD_CFG_FOREIGN_IMPORT:
274159024Sobrien		sx_xlock(&sc->mfi_config_lock);
274259024Sobrien		return (1);
274377298Sobrien	default:
274477298Sobrien		return (0);
274577298Sobrien	}
274677298Sobrien}
274777298Sobrien
274859024Sobrienstatic void
274959024Sobrienmfi_config_unlock(struct mfi_softc *sc, int locked)
275077298Sobrien{
275177298Sobrien
275277298Sobrien	if (locked)
275377298Sobrien		sx_xunlock(&sc->mfi_config_lock);
275459024Sobrien}
275577298Sobrien
275677298Sobrien/*
275759024Sobrien * Perform pre-issue checks on commands from userland and possibly veto
275859024Sobrien * them.
275959024Sobrien */
276059024Sobrienstatic int
276159024Sobrienmfi_check_command_pre(struct mfi_softc *sc, struct mfi_command *cm)
276259024Sobrien{
276359024Sobrien	struct mfi_disk *ld, *ld2;
276459024Sobrien	int error;
276559024Sobrien	struct mfi_system_pd *syspd = NULL;
276660484Sobrien	uint16_t syspd_id;
276759024Sobrien	uint16_t *mbox;
276859024Sobrien
276959024Sobrien	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
277059024Sobrien	error = 0;
277159024Sobrien	switch (cm->cm_frame->dcmd.opcode) {
277259024Sobrien	case MFI_DCMD_LD_DELETE:
277359024Sobrien		TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link) {
277459024Sobrien			if (ld->ld_id == cm->cm_frame->dcmd.mbox[0])
277559024Sobrien				break;
277659024Sobrien		}
277759024Sobrien		if (ld == NULL)
277859024Sobrien			error = ENOENT;
277959024Sobrien		else
278059024Sobrien			error = mfi_disk_disable(ld);
278177298Sobrien		break;
278259024Sobrien	case MFI_DCMD_CFG_CLEAR:
278359024Sobrien		TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link) {
278459024Sobrien			error = mfi_disk_disable(ld);
278559024Sobrien			if (error)
278677298Sobrien				break;
278759024Sobrien		}
278859024Sobrien		if (error) {
278959024Sobrien			TAILQ_FOREACH(ld2, &sc->mfi_ld_tqh, ld_link) {
279059024Sobrien				if (ld2 == ld)
279159024Sobrien					break;
279259024Sobrien				mfi_disk_enable(ld2);
279359024Sobrien			}
279459024Sobrien		}
279559024Sobrien		break;
279659024Sobrien	case MFI_DCMD_PD_STATE_SET:
279759024Sobrien		mbox = (uint16_t *) cm->cm_frame->dcmd.mbox;
279859024Sobrien		syspd_id = mbox[0];
279959024Sobrien		if (mbox[2] == MFI_PD_STATE_UNCONFIGURED_GOOD) {
280059024Sobrien			TAILQ_FOREACH(syspd, &sc->mfi_syspd_tqh, pd_link) {
280159024Sobrien				if (syspd->pd_id == syspd_id)
280260484Sobrien					break;
280360484Sobrien			}
280459024Sobrien		}
280559024Sobrien		else
280660484Sobrien			break;
280759024Sobrien		if (syspd)
280877298Sobrien			error = mfi_syspd_disable(syspd);
280959024Sobrien		break;
281059024Sobrien	default:
281177298Sobrien		break;
281259024Sobrien	}
281359024Sobrien	return (error);
281460484Sobrien}
281559024Sobrien
281659024Sobrien/* Perform post-issue checks on commands from userland. */
281759024Sobrienstatic void
281859024Sobrienmfi_check_command_post(struct mfi_softc *sc, struct mfi_command *cm)
281959024Sobrien{
282059024Sobrien	struct mfi_disk *ld, *ldn;
282159024Sobrien	struct mfi_system_pd *syspd = NULL;
282259024Sobrien	uint16_t syspd_id;
282359024Sobrien	uint16_t *mbox;
282459024Sobrien
282559024Sobrien	switch (cm->cm_frame->dcmd.opcode) {
282659024Sobrien	case MFI_DCMD_LD_DELETE:
282759024Sobrien		TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link) {
282859024Sobrien			if (ld->ld_id == cm->cm_frame->dcmd.mbox[0])
282959024Sobrien				break;
283059024Sobrien		}
283159024Sobrien		KASSERT(ld != NULL, ("volume dissappeared"));
283259024Sobrien		if (cm->cm_frame->header.cmd_status == MFI_STAT_OK) {
283389857Sobrien			mtx_unlock(&sc->mfi_io_lock);
283459024Sobrien			mtx_lock(&Giant);
283559024Sobrien			device_delete_child(sc->mfi_dev, ld->ld_dev);
283659024Sobrien			mtx_unlock(&Giant);
283759024Sobrien			mtx_lock(&sc->mfi_io_lock);
283859024Sobrien		} else
283959024Sobrien			mfi_disk_enable(ld);
284059024Sobrien		break;
284159024Sobrien	case MFI_DCMD_CFG_CLEAR:
284259024Sobrien		if (cm->cm_frame->header.cmd_status == MFI_STAT_OK) {
284359024Sobrien			mtx_unlock(&sc->mfi_io_lock);
284459024Sobrien			mtx_lock(&Giant);
284559024Sobrien			TAILQ_FOREACH_SAFE(ld, &sc->mfi_ld_tqh, ld_link, ldn) {
284659024Sobrien				device_delete_child(sc->mfi_dev, ld->ld_dev);
284759024Sobrien			}
284859024Sobrien			mtx_unlock(&Giant);
284959024Sobrien			mtx_lock(&sc->mfi_io_lock);
285059024Sobrien		} else {
285159024Sobrien			TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link)
285259024Sobrien				mfi_disk_enable(ld);
285359024Sobrien		}
285459024Sobrien		break;
285559024Sobrien	case MFI_DCMD_CFG_ADD:
285659024Sobrien		mfi_ldprobe(sc);
285759024Sobrien		break;
285859024Sobrien	case MFI_DCMD_CFG_FOREIGN_IMPORT:
285959024Sobrien		mfi_ldprobe(sc);
286059024Sobrien		break;
286159024Sobrien	case MFI_DCMD_PD_STATE_SET:
286259024Sobrien		mbox = (uint16_t *) cm->cm_frame->dcmd.mbox;
286359024Sobrien		syspd_id = mbox[0];
286459024Sobrien		if (mbox[2] == MFI_PD_STATE_UNCONFIGURED_GOOD) {
286559024Sobrien			TAILQ_FOREACH(syspd, &sc->mfi_syspd_tqh,pd_link) {
286659024Sobrien				if (syspd->pd_id == syspd_id)
286759024Sobrien					break;
286859024Sobrien			}
286959024Sobrien		}
287059024Sobrien		else
287159024Sobrien			break;
287259024Sobrien		/* If the transition fails then enable the syspd again */
287359024Sobrien		if (syspd && cm->cm_frame->header.cmd_status != MFI_STAT_OK)
287459024Sobrien			mfi_syspd_enable(syspd);
287559024Sobrien		break;
287659024Sobrien	}
287759024Sobrien}
287859024Sobrien
287959024Sobrienstatic int
288059024Sobrienmfi_check_for_sscd(struct mfi_softc *sc, struct mfi_command *cm)
288159024Sobrien{
288259024Sobrien	struct mfi_config_data *conf_data;
288359024Sobrien	struct mfi_command *ld_cm = NULL;
288459024Sobrien	struct mfi_ld_info *ld_info = NULL;
288559024Sobrien	struct mfi_ld_config *ld;
288659024Sobrien	char *p;
288759024Sobrien	int error = 0;
288859024Sobrien
288959024Sobrien	conf_data = (struct mfi_config_data *)cm->cm_data;
289059024Sobrien
289159024Sobrien	if (cm->cm_frame->dcmd.opcode == MFI_DCMD_CFG_ADD) {
289259024Sobrien		p = (char *)conf_data->array;
289359024Sobrien		p += conf_data->array_size * conf_data->array_count;
289459024Sobrien		ld = (struct mfi_ld_config *)p;
289559024Sobrien		if (ld->params.isSSCD == 1)
289659024Sobrien			error = 1;
289759024Sobrien	} else if (cm->cm_frame->dcmd.opcode == MFI_DCMD_LD_DELETE) {
289860484Sobrien		error = mfi_dcmd_command (sc, &ld_cm, MFI_DCMD_LD_GET_INFO,
289959024Sobrien		    (void **)&ld_info, sizeof(*ld_info));
290059024Sobrien		if (error) {
290159024Sobrien			device_printf(sc->mfi_dev, "Failed to allocate"
290259024Sobrien			    "MFI_DCMD_LD_GET_INFO %d", error);
290359024Sobrien			if (ld_info)
290459024Sobrien				free(ld_info, M_MFIBUF);
290559024Sobrien			return 0;
290659024Sobrien		}
290759024Sobrien		ld_cm->cm_flags = MFI_CMD_DATAIN;
290859024Sobrien		ld_cm->cm_frame->dcmd.mbox[0]= cm->cm_frame->dcmd.mbox[0];
290959024Sobrien		ld_cm->cm_frame->header.target_id = cm->cm_frame->dcmd.mbox[0];
291059024Sobrien		if (mfi_wait_command(sc, ld_cm) != 0) {
291159024Sobrien			device_printf(sc->mfi_dev, "failed to get log drv\n");
291259024Sobrien			mfi_release_command(ld_cm);
291359024Sobrien			free(ld_info, M_MFIBUF);
291459024Sobrien			return 0;
291559024Sobrien		}
291659024Sobrien
291777298Sobrien		if (ld_cm->cm_frame->header.cmd_status != MFI_STAT_OK) {
291859024Sobrien			free(ld_info, M_MFIBUF);
291959024Sobrien			mfi_release_command(ld_cm);
292059024Sobrien			return 0;
292159024Sobrien		}
292259024Sobrien		else
292377298Sobrien			ld_info = (struct mfi_ld_info *)ld_cm->cm_private;
292459024Sobrien
292559024Sobrien		if (ld_info->ld_config.params.isSSCD == 1)
292677298Sobrien			error = 1;
292777298Sobrien
292877298Sobrien		mfi_release_command(ld_cm);
292959024Sobrien		free(ld_info, M_MFIBUF);
293059024Sobrien
293159024Sobrien	}
293259024Sobrien	return error;
2933218822Sdim}
293459024Sobrien
293559024Sobrienstatic int
293659024Sobrienmfi_stp_cmd(struct mfi_softc *sc, struct mfi_command *cm,caddr_t arg)
293760484Sobrien{
293860484Sobrien	uint8_t i;
293959024Sobrien	struct mfi_ioc_packet *ioc;
294059024Sobrien	ioc = (struct mfi_ioc_packet *)arg;
294159024Sobrien	int sge_size, error;
294259024Sobrien	struct megasas_sge *kern_sge;
294377298Sobrien
294477298Sobrien	memset(sc->kbuff_arr, 0, sizeof(sc->kbuff_arr));
294577298Sobrien	kern_sge =(struct megasas_sge *) ((uintptr_t)cm->cm_frame + ioc->mfi_sgl_off);
294677298Sobrien	cm->cm_frame->header.sg_count = ioc->mfi_sge_count;
294759024Sobrien
294859024Sobrien	if (sizeof(bus_addr_t) == 8) {
294977298Sobrien		cm->cm_frame->header.flags |= MFI_FRAME_SGL64;
295077298Sobrien		cm->cm_extra_frames = 2;
295159024Sobrien		sge_size = sizeof(struct mfi_sg64);
295277298Sobrien	} else {
295377298Sobrien		cm->cm_extra_frames =  (cm->cm_total_frame_size - 1) / MFI_FRAME_SIZE;
295477298Sobrien		sge_size = sizeof(struct mfi_sg32);
295577298Sobrien	}
295659024Sobrien
295777298Sobrien	cm->cm_total_frame_size += (sge_size * ioc->mfi_sge_count);
295859024Sobrien	for (i = 0; i < ioc->mfi_sge_count; i++) {
295959024Sobrien			if (bus_dma_tag_create( sc->mfi_parent_dmat,	/* parent */
296059024Sobrien			1, 0,			/* algnmnt, boundary */
296159024Sobrien			BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
296259024Sobrien			BUS_SPACE_MAXADDR,	/* highaddr */
296359024Sobrien			NULL, NULL,		/* filter, filterarg */
296459024Sobrien			ioc->mfi_sgl[i].iov_len,/* maxsize */
296559024Sobrien			2,			/* nsegments */
296677298Sobrien			ioc->mfi_sgl[i].iov_len,/* maxsegsize */
296759024Sobrien			BUS_DMA_ALLOCNOW,	/* flags */
296859024Sobrien			NULL, NULL,		/* lockfunc, lockarg */
296959024Sobrien			&sc->mfi_kbuff_arr_dmat[i])) {
297059024Sobrien			device_printf(sc->mfi_dev,
297159024Sobrien			    "Cannot allocate mfi_kbuff_arr_dmat tag\n");
297259024Sobrien			return (ENOMEM);
297359024Sobrien		}
297459024Sobrien
297559024Sobrien		if (bus_dmamem_alloc(sc->mfi_kbuff_arr_dmat[i],
297659024Sobrien		    (void **)&sc->kbuff_arr[i], BUS_DMA_NOWAIT,
297759024Sobrien		    &sc->mfi_kbuff_arr_dmamap[i])) {
297859024Sobrien			device_printf(sc->mfi_dev,
297959024Sobrien			    "Cannot allocate mfi_kbuff_arr_dmamap memory\n");
298059024Sobrien			return (ENOMEM);
298159024Sobrien		}
298259024Sobrien
298359024Sobrien		bus_dmamap_load(sc->mfi_kbuff_arr_dmat[i],
298459024Sobrien		    sc->mfi_kbuff_arr_dmamap[i], sc->kbuff_arr[i],
298559024Sobrien		    ioc->mfi_sgl[i].iov_len, mfi_addr_cb,
298659024Sobrien		    &sc->mfi_kbuff_arr_busaddr[i], 0);
298759024Sobrien
298859024Sobrien		if (!sc->kbuff_arr[i]) {
298959024Sobrien			device_printf(sc->mfi_dev,
299059024Sobrien			    "Could not allocate memory for kbuff_arr info\n");
299159024Sobrien			return -1;
299259024Sobrien		}
299359024Sobrien		kern_sge[i].phys_addr = sc->mfi_kbuff_arr_busaddr[i];
299459024Sobrien		kern_sge[i].length = ioc->mfi_sgl[i].iov_len;
299559024Sobrien
299659024Sobrien		if (sizeof(bus_addr_t) == 8) {
299759024Sobrien			cm->cm_frame->stp.sgl.sg64[i].addr =
299860484Sobrien			    kern_sge[i].phys_addr;
299959024Sobrien			cm->cm_frame->stp.sgl.sg64[i].len =
300059024Sobrien			    ioc->mfi_sgl[i].iov_len;
300159024Sobrien		} else {
300259024Sobrien			cm->cm_frame->stp.sgl.sg32[i].addr =
300359024Sobrien			    kern_sge[i].phys_addr;
300459024Sobrien			cm->cm_frame->stp.sgl.sg32[i].len =
300559024Sobrien			    ioc->mfi_sgl[i].iov_len;
300659024Sobrien		}
300759024Sobrien
300859024Sobrien		error = copyin(ioc->mfi_sgl[i].iov_base,
300959024Sobrien		    sc->kbuff_arr[i],
301077298Sobrien		    ioc->mfi_sgl[i].iov_len);
301177298Sobrien		if (error != 0) {
301259024Sobrien			device_printf(sc->mfi_dev, "Copy in failed\n");
301359024Sobrien			return error;
301459024Sobrien		}
301559024Sobrien	}
301659024Sobrien
301759024Sobrien	cm->cm_flags |=MFI_CMD_MAPPED;
301859024Sobrien	return 0;
301977298Sobrien}
302077298Sobrien
302159024Sobrienstatic int
302259024Sobrienmfi_user_command(struct mfi_softc *sc, struct mfi_ioc_passthru *ioc)
302359024Sobrien{
302477298Sobrien	struct mfi_command *cm;
302559024Sobrien	struct mfi_dcmd_frame *dcmd;
302659024Sobrien	void *ioc_buf = NULL;
302759024Sobrien	uint32_t context;
302859024Sobrien	int error = 0, locked;
302959024Sobrien
303059024Sobrien
303159024Sobrien	if (ioc->buf_size > 0) {
303259024Sobrien		if (ioc->buf_size > 1024 * 1024)
303359024Sobrien			return (ENOMEM);
303459024Sobrien		ioc_buf = malloc(ioc->buf_size, M_MFIBUF, M_WAITOK);
303559024Sobrien		error = copyin(ioc->buf, ioc_buf, ioc->buf_size);
303659024Sobrien		if (error) {
303759024Sobrien			device_printf(sc->mfi_dev, "failed to copyin\n");
303859024Sobrien			free(ioc_buf, M_MFIBUF);
303960484Sobrien			return (error);
304060484Sobrien		}
304177298Sobrien	}
304277298Sobrien
304360484Sobrien	locked = mfi_config_lock(sc, ioc->ioc_frame.opcode);
304460484Sobrien
304559024Sobrien	mtx_lock(&sc->mfi_io_lock);
304659024Sobrien	while ((cm = mfi_dequeue_free(sc)) == NULL)
304759024Sobrien		msleep(mfi_user_command, &sc->mfi_io_lock, 0, "mfiioc", hz);
304859024Sobrien
304977298Sobrien	/* Save context for later */
305059024Sobrien	context = cm->cm_frame->header.context;
305189857Sobrien
3052218822Sdim	dcmd = &cm->cm_frame->dcmd;
305359024Sobrien	bcopy(&ioc->ioc_frame, dcmd, sizeof(struct mfi_dcmd_frame));
305489857Sobrien
3055130561Sobrien	cm->cm_sg = &dcmd->sgl;
305659024Sobrien	cm->cm_total_frame_size = MFI_DCMD_FRAME_SIZE;
305759024Sobrien	cm->cm_data = ioc_buf;
305889857Sobrien	cm->cm_len = ioc->buf_size;
305959024Sobrien
306059024Sobrien	/* restore context */
306159024Sobrien	cm->cm_frame->header.context = context;
306259024Sobrien
306377298Sobrien	/* Cheat since we don't know if we're writing or reading */
306459024Sobrien	cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_DATAOUT;
306559024Sobrien
3066130561Sobrien	error = mfi_check_command_pre(sc, cm);
306759024Sobrien	if (error)
3068218822Sdim		goto out;
3069218822Sdim
3070218822Sdim	error = mfi_wait_command(sc, cm);
3071218822Sdim	if (error) {
3072218822Sdim		device_printf(sc->mfi_dev, "ioctl failed %d\n", error);
3073218822Sdim		goto out;
3074218822Sdim	}
3075218822Sdim	bcopy(dcmd, &ioc->ioc_frame, sizeof(struct mfi_dcmd_frame));
3076218822Sdim	mfi_check_command_post(sc, cm);
3077218822Sdimout:
3078218822Sdim	mfi_release_command(cm);
3079218822Sdim	mtx_unlock(&sc->mfi_io_lock);
3080218822Sdim	mfi_config_unlock(sc, locked);
3081218822Sdim	if (ioc->buf_size > 0)
3082218822Sdim		error = copyout(ioc_buf, ioc->buf, ioc->buf_size);
3083218822Sdim	if (ioc_buf)
3084218822Sdim		free(ioc_buf, M_MFIBUF);
3085218822Sdim	return (error);
3086218822Sdim}
3087218822Sdim
3088218822Sdim#define	PTRIN(p)		((void *)(uintptr_t)(p))
3089218822Sdim
3090218822Sdimstatic int
3091218822Sdimmfi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
3092218822Sdim{
3093218822Sdim	struct mfi_softc *sc;
3094218822Sdim	union mfi_statrequest *ms;
3095218822Sdim	struct mfi_ioc_packet *ioc;
3096218822Sdim#ifdef COMPAT_FREEBSD32
3097218822Sdim	struct mfi_ioc_packet32 *ioc32;
3098218822Sdim#endif
3099218822Sdim	struct mfi_ioc_aen *aen;
3100218822Sdim	struct mfi_command *cm = NULL;
3101218822Sdim	uint32_t context = 0;
3102218822Sdim	union mfi_sense_ptr sense_ptr;
310359024Sobrien	uint8_t *data = NULL, *temp, *addr, skip_pre_post = 0;
310459024Sobrien	size_t len;
310559024Sobrien	int i, res;
310659024Sobrien	struct mfi_ioc_passthru *iop = (struct mfi_ioc_passthru *)arg;
310759024Sobrien#ifdef COMPAT_FREEBSD32
310859024Sobrien	struct mfi_ioc_passthru32 *iop32 = (struct mfi_ioc_passthru32 *)arg;
310959024Sobrien	struct mfi_ioc_passthru iop_swab;
311059024Sobrien#endif
311159024Sobrien	int error, locked;
311259024Sobrien	union mfi_sgl *sgl;
311359024Sobrien	sc = dev->si_drv1;
311459024Sobrien	error = 0;
311559024Sobrien
311659024Sobrien	if (sc->adpreset)
311759024Sobrien		return EBUSY;
311859024Sobrien
311959024Sobrien	if (sc->hw_crit_error)
312059024Sobrien		return EBUSY;
312159024Sobrien
312259024Sobrien	if (sc->issuepend_done == 0)
312359024Sobrien		return EBUSY;
312459024Sobrien
312559024Sobrien	switch (cmd) {
312659024Sobrien	case MFIIO_STATS:
312759024Sobrien		ms = (union mfi_statrequest *)arg;
312859024Sobrien		switch (ms->ms_item) {
312959024Sobrien		case MFIQ_FREE:
313059024Sobrien		case MFIQ_BIO:
313160484Sobrien		case MFIQ_READY:
313259024Sobrien		case MFIQ_BUSY:
313360484Sobrien			bcopy(&sc->mfi_qstat[ms->ms_item], &ms->ms_qstat,
313460484Sobrien			    sizeof(struct mfi_qstat));
313560484Sobrien			break;
313660484Sobrien		default:
313760484Sobrien			error = ENOIOCTL;
313860484Sobrien			break;
313960484Sobrien		}
314060484Sobrien		break;
314160484Sobrien	case MFIIO_QUERY_DISK:
314260484Sobrien	{
314360484Sobrien		struct mfi_query_disk *qd;
314460484Sobrien		struct mfi_disk *ld;
314560484Sobrien
314660484Sobrien		qd = (struct mfi_query_disk *)arg;
314760484Sobrien		mtx_lock(&sc->mfi_io_lock);
314859024Sobrien		TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link) {
314959024Sobrien			if (ld->ld_id == qd->array_id)
315059024Sobrien				break;
315159024Sobrien		}
315278828Sobrien		if (ld == NULL) {
315378828Sobrien			qd->present = 0;
315459024Sobrien			mtx_unlock(&sc->mfi_io_lock);
315559024Sobrien			return (0);
315659024Sobrien		}
315760484Sobrien		qd->present = 1;
315878828Sobrien		if (ld->ld_flags & MFI_DISK_FLAGS_OPEN)
315960484Sobrien			qd->open = 1;
316059024Sobrien		bzero(qd->devname, SPECNAMELEN + 1);
316159024Sobrien		snprintf(qd->devname, SPECNAMELEN, "mfid%d", ld->ld_unit);
316259024Sobrien		mtx_unlock(&sc->mfi_io_lock);
316378828Sobrien		break;
316478828Sobrien	}
316559024Sobrien	case MFI_CMD:
316659024Sobrien#ifdef COMPAT_FREEBSD32
316759024Sobrien	case MFI_CMD32:
316877298Sobrien#endif
316960484Sobrien		{
317060484Sobrien		devclass_t devclass;
317160484Sobrien		ioc = (struct mfi_ioc_packet *)arg;
317289857Sobrien		int adapter;
317360484Sobrien
317459024Sobrien		adapter = ioc->mfi_adapter_no;
317559024Sobrien		if (device_get_unit(sc->mfi_dev) == 0 && adapter != 0) {
317659024Sobrien			devclass = devclass_find("mfi");
317759024Sobrien			sc = devclass_get_softc(devclass, adapter);
317859024Sobrien		}
317959024Sobrien		mtx_lock(&sc->mfi_io_lock);
318059024Sobrien		if ((cm = mfi_dequeue_free(sc)) == NULL) {
318159024Sobrien			mtx_unlock(&sc->mfi_io_lock);
318277298Sobrien			return (EBUSY);
318359024Sobrien		}
318459024Sobrien		mtx_unlock(&sc->mfi_io_lock);
318559024Sobrien		locked = 0;
318659024Sobrien
318759024Sobrien		/*
318859024Sobrien		 * save off original context since copying from user
318959024Sobrien		 * will clobber some data
319059024Sobrien		 */
319160484Sobrien		context = cm->cm_frame->header.context;
319259024Sobrien		cm->cm_frame->header.context = cm->cm_index;
319377298Sobrien
319459024Sobrien		bcopy(ioc->mfi_frame.raw, cm->cm_frame,
319577298Sobrien		    2 * MEGAMFI_FRAME_SIZE);
319677298Sobrien		cm->cm_total_frame_size = (sizeof(union mfi_sgl)
319777298Sobrien		    * ioc->mfi_sge_count) + ioc->mfi_sgl_off;
319877298Sobrien		cm->cm_frame->header.scsi_status = 0;
319977298Sobrien		cm->cm_frame->header.pad0 = 0;
320077298Sobrien		if (ioc->mfi_sge_count) {
320177298Sobrien			cm->cm_sg =
320277298Sobrien			    (union mfi_sgl *)&cm->cm_frame->bytes[ioc->mfi_sgl_off];
320377298Sobrien		}
320477298Sobrien		sgl = cm->cm_sg;
320577298Sobrien		cm->cm_flags = 0;
320677298Sobrien		if (cm->cm_frame->header.flags & MFI_FRAME_DATAIN)
320777298Sobrien			cm->cm_flags |= MFI_CMD_DATAIN;
320877298Sobrien		if (cm->cm_frame->header.flags & MFI_FRAME_DATAOUT)
320977298Sobrien			cm->cm_flags |= MFI_CMD_DATAOUT;
321077298Sobrien		/* Legacy app shim */
321177298Sobrien		if (cm->cm_flags == 0)
321277298Sobrien			cm->cm_flags |= MFI_CMD_DATAIN | MFI_CMD_DATAOUT;
321377298Sobrien		cm->cm_len = cm->cm_frame->header.data_len;
321477298Sobrien		if (cm->cm_frame->header.cmd == MFI_CMD_STP) {
321577298Sobrien#ifdef COMPAT_FREEBSD32
321677298Sobrien			if (cmd == MFI_CMD) {
321777298Sobrien#endif
321877298Sobrien				/* Native */
321977298Sobrien				cm->cm_stp_len = ioc->mfi_sgl[0].iov_len;
322077298Sobrien#ifdef COMPAT_FREEBSD32
322177298Sobrien			} else {
322277298Sobrien				/* 32bit on 64bit */
322377298Sobrien				ioc32 = (struct mfi_ioc_packet32 *)ioc;
322477298Sobrien				cm->cm_stp_len = ioc32->mfi_sgl[0].iov_len;
322577298Sobrien			}
322677298Sobrien#endif
322777298Sobrien			cm->cm_len += cm->cm_stp_len;
322877298Sobrien		}
322977298Sobrien		if (cm->cm_len &&
323077298Sobrien		    (cm->cm_flags & (MFI_CMD_DATAIN | MFI_CMD_DATAOUT))) {
323177298Sobrien			cm->cm_data = data = malloc(cm->cm_len, M_MFIBUF,
323277298Sobrien			    M_WAITOK | M_ZERO);
323377298Sobrien		} else {
323477298Sobrien			cm->cm_data = 0;
323577298Sobrien		}
323677298Sobrien
323777298Sobrien		/* restore header context */
323877298Sobrien		cm->cm_frame->header.context = context;
323977298Sobrien
324077298Sobrien		if (cm->cm_frame->header.cmd == MFI_CMD_STP) {
324177298Sobrien			res = mfi_stp_cmd(sc, cm, arg);
324277298Sobrien			if (res != 0)
324377298Sobrien				goto out;
324477298Sobrien		} else {
324577298Sobrien			temp = data;
324677298Sobrien			if ((cm->cm_flags & MFI_CMD_DATAOUT) ||
324777298Sobrien			    (cm->cm_frame->header.cmd == MFI_CMD_STP)) {
324877298Sobrien				for (i = 0; i < ioc->mfi_sge_count; i++) {
324977298Sobrien#ifdef COMPAT_FREEBSD32
325077298Sobrien					if (cmd == MFI_CMD) {
325177298Sobrien#endif
325277298Sobrien						/* Native */
325377298Sobrien						addr = ioc->mfi_sgl[i].iov_base;
325477298Sobrien						len = ioc->mfi_sgl[i].iov_len;
325577298Sobrien#ifdef COMPAT_FREEBSD32
325677298Sobrien					} else {
325777298Sobrien						/* 32bit on 64bit */
325877298Sobrien						ioc32 = (struct mfi_ioc_packet32 *)ioc;
325977298Sobrien						addr = PTRIN(ioc32->mfi_sgl[i].iov_base);
326077298Sobrien						len = ioc32->mfi_sgl[i].iov_len;
326177298Sobrien					}
326277298Sobrien#endif
326377298Sobrien					error = copyin(addr, temp, len);
326477298Sobrien					if (error != 0) {
326577298Sobrien						device_printf(sc->mfi_dev,
326677298Sobrien						    "Copy in failed\n");
326777298Sobrien						goto out;
326877298Sobrien					}
326977298Sobrien					temp = &temp[len];
327077298Sobrien				}
327177298Sobrien			}
327277298Sobrien		}
327377298Sobrien
327477298Sobrien		if (cm->cm_frame->header.cmd == MFI_CMD_DCMD)
327577298Sobrien			locked = mfi_config_lock(sc,
327677298Sobrien			     cm->cm_frame->dcmd.opcode);
327777298Sobrien
327859024Sobrien		if (cm->cm_frame->header.cmd == MFI_CMD_PD_SCSI_IO) {
327959024Sobrien			cm->cm_frame->pass.sense_addr_lo =
328059024Sobrien			    (uint32_t)cm->cm_sense_busaddr;
328159024Sobrien			cm->cm_frame->pass.sense_addr_hi =
328260484Sobrien			    (uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32);
328360484Sobrien		}
328459024Sobrien		mtx_lock(&sc->mfi_io_lock);
328559024Sobrien		skip_pre_post = mfi_check_for_sscd (sc, cm);
328659024Sobrien		if (!skip_pre_post) {
328759024Sobrien			error = mfi_check_command_pre(sc, cm);
328859024Sobrien			if (error) {
328960484Sobrien				mtx_unlock(&sc->mfi_io_lock);
329060484Sobrien				goto out;
329159024Sobrien			}
329259024Sobrien		}
329359024Sobrien		if ((error = mfi_wait_command(sc, cm)) != 0) {
329459024Sobrien			device_printf(sc->mfi_dev,
329559024Sobrien			    "Controller polled failed\n");
329660484Sobrien			mtx_unlock(&sc->mfi_io_lock);
329760484Sobrien			goto out;
329859024Sobrien		}
329959024Sobrien		if (!skip_pre_post) {
330059024Sobrien			mfi_check_command_post(sc, cm);
330159024Sobrien		}
330259024Sobrien		mtx_unlock(&sc->mfi_io_lock);
330360484Sobrien
330460484Sobrien		if (cm->cm_frame->header.cmd != MFI_CMD_STP) {
330559024Sobrien			temp = data;
330659024Sobrien			if ((cm->cm_flags & MFI_CMD_DATAIN) ||
330759024Sobrien			    (cm->cm_frame->header.cmd == MFI_CMD_STP)) {
330859024Sobrien				for (i = 0; i < ioc->mfi_sge_count; i++) {
330959024Sobrien#ifdef COMPAT_FREEBSD32
331060484Sobrien					if (cmd == MFI_CMD) {
331160484Sobrien#endif
331259024Sobrien						/* Native */
331359024Sobrien						addr = ioc->mfi_sgl[i].iov_base;
331459024Sobrien						len = ioc->mfi_sgl[i].iov_len;
331559024Sobrien#ifdef COMPAT_FREEBSD32
3316218822Sdim					} else {
3317218822Sdim						/* 32bit on 64bit */
3318218822Sdim						ioc32 = (struct mfi_ioc_packet32 *)ioc;
331960484Sobrien						addr = PTRIN(ioc32->mfi_sgl[i].iov_base);
332060484Sobrien						len = ioc32->mfi_sgl[i].iov_len;
332159024Sobrien					}
332259024Sobrien#endif
332359024Sobrien					error = copyout(temp, addr, len);
332459024Sobrien					if (error != 0) {
332559024Sobrien						device_printf(sc->mfi_dev,
332659024Sobrien						    "Copy out failed\n");
3327218822Sdim						goto out;
3328218822Sdim					}
3329218822Sdim					temp = &temp[len];
333060484Sobrien				}
333160484Sobrien			}
333259024Sobrien		}
333359024Sobrien
333459024Sobrien		if (ioc->mfi_sense_len) {
333559024Sobrien			/* get user-space sense ptr then copy out sense */
333659024Sobrien			bcopy(&ioc->mfi_frame.raw[ioc->mfi_sense_off],
333759024Sobrien			    &sense_ptr.sense_ptr_data[0],
333859024Sobrien			    sizeof(sense_ptr.sense_ptr_data));
333977298Sobrien#ifdef COMPAT_FREEBSD32
334059024Sobrien			if (cmd != MFI_CMD) {
334159024Sobrien				/*
334259024Sobrien				 * not 64bit native so zero out any address
334359024Sobrien				 * over 32bit */
334489857Sobrien				sense_ptr.addr.high = 0;
334559024Sobrien			}
334689857Sobrien#endif
334789857Sobrien			error = copyout(cm->cm_sense, sense_ptr.user_space,
334859024Sobrien			    ioc->mfi_sense_len);
334959024Sobrien			if (error != 0) {
335059024Sobrien				device_printf(sc->mfi_dev,
335159024Sobrien				    "Copy out failed\n");
335260484Sobrien				goto out;
335360484Sobrien			}
335459024Sobrien		}
335559024Sobrien
335659024Sobrien		ioc->mfi_frame.hdr.cmd_status = cm->cm_frame->header.cmd_status;
335759024Sobrienout:
335859024Sobrien		mfi_config_unlock(sc, locked);
335977298Sobrien		if (data)
336059024Sobrien			free(data, M_MFIBUF);
336159024Sobrien		if (cm->cm_frame->header.cmd == MFI_CMD_STP) {
336259024Sobrien			for (i = 0; i < 2; i++) {
336389857Sobrien				if (sc->kbuff_arr[i]) {
336459024Sobrien					if (sc->mfi_kbuff_arr_busaddr != 0)
336589857Sobrien						bus_dmamap_unload(
336689857Sobrien						    sc->mfi_kbuff_arr_dmat[i],
336759024Sobrien						    sc->mfi_kbuff_arr_dmamap[i]
336859024Sobrien						    );
336960484Sobrien					if (sc->kbuff_arr[i] != NULL)
337060484Sobrien						bus_dmamem_free(
337160484Sobrien						    sc->mfi_kbuff_arr_dmat[i],
337277298Sobrien						    sc->kbuff_arr[i],
337360484Sobrien						    sc->mfi_kbuff_arr_dmamap[i]
337459024Sobrien						    );
337559024Sobrien					if (sc->mfi_kbuff_arr_dmat[i] != NULL)
337660484Sobrien						bus_dma_tag_destroy(
337760484Sobrien						    sc->mfi_kbuff_arr_dmat[i]);
337859024Sobrien				}
337959024Sobrien			}
338059024Sobrien		}
338159024Sobrien		if (cm) {
338259024Sobrien			mtx_lock(&sc->mfi_io_lock);
338377298Sobrien			mfi_release_command(cm);
338459024Sobrien			mtx_unlock(&sc->mfi_io_lock);
338559024Sobrien		}
338659024Sobrien
338759024Sobrien		break;
338859024Sobrien		}
338959024Sobrien	case MFI_SET_AEN:
339059024Sobrien		aen = (struct mfi_ioc_aen *)arg;
339159024Sobrien		mtx_lock(&sc->mfi_io_lock);
339259024Sobrien		error = mfi_aen_register(sc, aen->aen_seq_num,
339359024Sobrien		    aen->aen_class_locale);
339459024Sobrien		mtx_unlock(&sc->mfi_io_lock);
339559024Sobrien
339659024Sobrien		break;
339759024Sobrien	case MFI_LINUX_CMD_2: /* Firmware Linux ioctl shim */
339859024Sobrien		{
339959024Sobrien			devclass_t devclass;
340059024Sobrien			struct mfi_linux_ioc_packet l_ioc;
340159024Sobrien			int adapter;
340259024Sobrien
340359024Sobrien			devclass = devclass_find("mfi");
340459024Sobrien			if (devclass == NULL)
340559024Sobrien				return (ENOENT);
340659024Sobrien
340759024Sobrien			error = copyin(arg, &l_ioc, sizeof(l_ioc));
340859024Sobrien			if (error)
340959024Sobrien				return (error);
341077298Sobrien			adapter = l_ioc.lioc_adapter_no;
341159024Sobrien			sc = devclass_get_softc(devclass, adapter);
341259024Sobrien			if (sc == NULL)
341359024Sobrien				return (ENOENT);
341459024Sobrien			return (mfi_linux_ioctl_int(sc->mfi_cdev,
341559024Sobrien			    cmd, arg, flag, td));
341659024Sobrien			break;
341759024Sobrien		}
341859024Sobrien	case MFI_LINUX_SET_AEN_2: /* AEN Linux ioctl shim */
341959024Sobrien		{
342059024Sobrien			devclass_t devclass;
342159024Sobrien			struct mfi_linux_ioc_aen l_aen;
342259024Sobrien			int adapter;
342360484Sobrien
342459024Sobrien			devclass = devclass_find("mfi");
342559024Sobrien			if (devclass == NULL)
342659024Sobrien				return (ENOENT);
342759024Sobrien
342859024Sobrien			error = copyin(arg, &l_aen, sizeof(l_aen));
342959024Sobrien			if (error)
343059024Sobrien				return (error);
343159024Sobrien			adapter = l_aen.laen_adapter_no;
343259024Sobrien			sc = devclass_get_softc(devclass, adapter);
343359024Sobrien			if (sc == NULL)
343459024Sobrien				return (ENOENT);
343559024Sobrien			return (mfi_linux_ioctl_int(sc->mfi_cdev,
343659024Sobrien			    cmd, arg, flag, td));
343759024Sobrien			break;
343859024Sobrien		}
343959024Sobrien#ifdef COMPAT_FREEBSD32
344059024Sobrien	case MFIIO_PASSTHRU32:
344177298Sobrien		if (!SV_CURPROC_FLAG(SV_ILP32)) {
344260484Sobrien			error = ENOTTY;
344359024Sobrien			break;
3444218822Sdim		}
344559024Sobrien		iop_swab.ioc_frame	= iop32->ioc_frame;
344659024Sobrien		iop_swab.buf_size	= iop32->buf_size;
344760484Sobrien		iop_swab.buf		= PTRIN(iop32->buf);
344859024Sobrien		iop			= &iop_swab;
344959024Sobrien		/* FALLTHROUGH */
345059024Sobrien#endif
345160484Sobrien	case MFIIO_PASSTHRU:
345260484Sobrien		error = mfi_user_command(sc, iop);
345359024Sobrien#ifdef COMPAT_FREEBSD32
345460484Sobrien		if (cmd == MFIIO_PASSTHRU32)
345560484Sobrien			iop32->ioc_frame = iop_swab.ioc_frame;
345659024Sobrien#endif
345759024Sobrien		break;
345859024Sobrien	default:
345959024Sobrien		device_printf(sc->mfi_dev, "IOCTL 0x%lx not handled\n", cmd);
346059024Sobrien		error = ENOTTY;
346159024Sobrien		break;
346259024Sobrien	}
346359024Sobrien
346459024Sobrien	return (error);
346559024Sobrien}
346660484Sobrien
346759024Sobrienstatic int
346859024Sobrienmfi_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
346959024Sobrien{
347059024Sobrien	struct mfi_softc *sc;
347159024Sobrien	struct mfi_linux_ioc_packet l_ioc;
347259024Sobrien	struct mfi_linux_ioc_aen l_aen;
347359024Sobrien	struct mfi_command *cm = NULL;
347459024Sobrien	struct mfi_aen *mfi_aen_entry;
347559024Sobrien	union mfi_sense_ptr sense_ptr;
347659024Sobrien	uint32_t context = 0;
347759024Sobrien	uint8_t *data = NULL, *temp;
347859024Sobrien	int i;
347959024Sobrien	int error, locked;
348059024Sobrien
348159024Sobrien	sc = dev->si_drv1;
348259024Sobrien	error = 0;
348359024Sobrien	switch (cmd) {
348459024Sobrien	case MFI_LINUX_CMD_2: /* Firmware Linux ioctl shim */
348559024Sobrien		error = copyin(arg, &l_ioc, sizeof(l_ioc));
348659024Sobrien		if (error != 0)
348759024Sobrien			return (error);
348860484Sobrien
348960484Sobrien		if (l_ioc.lioc_sge_count > MAX_LINUX_IOCTL_SGE) {
349078828Sobrien			return (EINVAL);
349178828Sobrien		}
349278828Sobrien
349389857Sobrien		mtx_lock(&sc->mfi_io_lock);
349489857Sobrien		if ((cm = mfi_dequeue_free(sc)) == NULL) {
349589857Sobrien			mtx_unlock(&sc->mfi_io_lock);
349689857Sobrien			return (EBUSY);
349789857Sobrien		}
349889857Sobrien		mtx_unlock(&sc->mfi_io_lock);
349960484Sobrien		locked = 0;
350060484Sobrien
3501130561Sobrien		/*
3502130561Sobrien		 * save off original context since copying from user
3503130561Sobrien		 * will clobber some data
3504130561Sobrien		 */
3505130561Sobrien		context = cm->cm_frame->header.context;
3506130561Sobrien
3507130561Sobrien		bcopy(l_ioc.lioc_frame.raw, cm->cm_frame,
3508130561Sobrien		      2 * MFI_DCMD_FRAME_SIZE);	/* this isn't quite right */
3509130561Sobrien		cm->cm_total_frame_size = (sizeof(union mfi_sgl)
3510130561Sobrien		      * l_ioc.lioc_sge_count) + l_ioc.lioc_sgl_off;
3511130561Sobrien		cm->cm_frame->header.scsi_status = 0;
3512130561Sobrien		cm->cm_frame->header.pad0 = 0;
3513130561Sobrien		if (l_ioc.lioc_sge_count)
3514130561Sobrien			cm->cm_sg =
3515130561Sobrien			    (union mfi_sgl *)&cm->cm_frame->bytes[l_ioc.lioc_sgl_off];
3516130561Sobrien		cm->cm_flags = 0;
3517130561Sobrien		if (cm->cm_frame->header.flags & MFI_FRAME_DATAIN)
3518130561Sobrien			cm->cm_flags |= MFI_CMD_DATAIN;
3519130561Sobrien		if (cm->cm_frame->header.flags & MFI_FRAME_DATAOUT)
3520130561Sobrien			cm->cm_flags |= MFI_CMD_DATAOUT;
352159024Sobrien		cm->cm_len = cm->cm_frame->header.data_len;
352259024Sobrien		if (cm->cm_len &&
352359024Sobrien		      (cm->cm_flags & (MFI_CMD_DATAIN | MFI_CMD_DATAOUT))) {
352459024Sobrien			cm->cm_data = data = malloc(cm->cm_len, M_MFIBUF,
352559024Sobrien			    M_WAITOK | M_ZERO);
352659024Sobrien		} else {
352759024Sobrien			cm->cm_data = 0;
352859024Sobrien		}
352959024Sobrien
353059024Sobrien		/* restore header context */
353159024Sobrien		cm->cm_frame->header.context = context;
353259024Sobrien
353359024Sobrien		temp = data;
353459024Sobrien		if (cm->cm_flags & MFI_CMD_DATAOUT) {
353559024Sobrien			for (i = 0; i < l_ioc.lioc_sge_count; i++) {
353659024Sobrien				error = copyin(PTRIN(l_ioc.lioc_sgl[i].iov_base),
3537218822Sdim				       temp,
3538218822Sdim				       l_ioc.lioc_sgl[i].iov_len);
3539218822Sdim				if (error != 0) {
3540218822Sdim					device_printf(sc->mfi_dev,
354159024Sobrien					    "Copy in failed\n");
354260484Sobrien					goto out;
354360484Sobrien				}
354459024Sobrien				temp = &temp[l_ioc.lioc_sgl[i].iov_len];
354559024Sobrien			}
354659024Sobrien		}
354759024Sobrien
354859024Sobrien		if (cm->cm_frame->header.cmd == MFI_CMD_DCMD)
3549130561Sobrien			locked = mfi_config_lock(sc, cm->cm_frame->dcmd.opcode);
355059024Sobrien
355159024Sobrien		if (cm->cm_frame->header.cmd == MFI_CMD_PD_SCSI_IO) {
355259024Sobrien			cm->cm_frame->pass.sense_addr_lo =
3553218822Sdim			    (uint32_t)cm->cm_sense_busaddr;
3554218822Sdim			cm->cm_frame->pass.sense_addr_hi =
3555218822Sdim			    (uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32);
3556218822Sdim		}
3557218822Sdim
3558218822Sdim		mtx_lock(&sc->mfi_io_lock);
3559218822Sdim		error = mfi_check_command_pre(sc, cm);
3560218822Sdim		if (error) {
3561218822Sdim			mtx_unlock(&sc->mfi_io_lock);
3562218822Sdim			goto out;
3563218822Sdim		}
356459024Sobrien
356559024Sobrien		if ((error = mfi_wait_command(sc, cm)) != 0) {
3566218822Sdim			device_printf(sc->mfi_dev,
3567218822Sdim			    "Controller polled failed\n");
3568218822Sdim			mtx_unlock(&sc->mfi_io_lock);
3569218822Sdim			goto out;
3570218822Sdim		}
3571218822Sdim
3572218822Sdim		mfi_check_command_post(sc, cm);
3573218822Sdim		mtx_unlock(&sc->mfi_io_lock);
3574218822Sdim
3575218822Sdim		temp = data;
3576218822Sdim		if (cm->cm_flags & MFI_CMD_DATAIN) {
357759024Sobrien			for (i = 0; i < l_ioc.lioc_sge_count; i++) {
357859024Sobrien				error = copyout(temp,
357959024Sobrien					PTRIN(l_ioc.lioc_sgl[i].iov_base),
358059024Sobrien					l_ioc.lioc_sgl[i].iov_len);
358159024Sobrien				if (error != 0) {
358259024Sobrien					device_printf(sc->mfi_dev,
358359024Sobrien					    "Copy out failed\n");
358459024Sobrien					goto out;
358577298Sobrien				}
358659024Sobrien				temp = &temp[l_ioc.lioc_sgl[i].iov_len];
3587218822Sdim			}
3588218822Sdim		}
3589218822Sdim
3590218822Sdim		if (l_ioc.lioc_sense_len) {
3591218822Sdim			/* get user-space sense ptr then copy out sense */
3592218822Sdim			bcopy(&((struct mfi_linux_ioc_packet*)arg)
3593218822Sdim                            ->lioc_frame.raw[l_ioc.lioc_sense_off],
3594218822Sdim			    &sense_ptr.sense_ptr_data[0],
3595218822Sdim			    sizeof(sense_ptr.sense_ptr_data));
3596218822Sdim#ifdef __amd64__
359760484Sobrien			/*
359860484Sobrien			 * only 32bit Linux support so zero out any
359960484Sobrien			 * address over 32bit
360060484Sobrien			 */
360159024Sobrien			sense_ptr.addr.high = 0;
360259024Sobrien#endif
360359024Sobrien			error = copyout(cm->cm_sense, sense_ptr.user_space,
360460484Sobrien			    l_ioc.lioc_sense_len);
360559024Sobrien			if (error != 0) {
360660484Sobrien				device_printf(sc->mfi_dev,
360760484Sobrien				    "Copy out failed\n");
360860484Sobrien				goto out;
360959024Sobrien			}
361059024Sobrien		}
361159024Sobrien
361259024Sobrien		error = copyout(&cm->cm_frame->header.cmd_status,
361359024Sobrien			&((struct mfi_linux_ioc_packet*)arg)
361459024Sobrien			->lioc_frame.hdr.cmd_status,
361559024Sobrien			1);
361659024Sobrien		if (error != 0) {
361759024Sobrien			device_printf(sc->mfi_dev,
361860484Sobrien				      "Copy out failed\n");
361960484Sobrien			goto out;
362060484Sobrien		}
362160484Sobrien
362260484Sobrienout:
362360484Sobrien		mfi_config_unlock(sc, locked);
362460484Sobrien		if (data)
362560484Sobrien			free(data, M_MFIBUF);
362659024Sobrien		if (cm) {
362759024Sobrien			mtx_lock(&sc->mfi_io_lock);
362859024Sobrien			mfi_release_command(cm);
362977298Sobrien			mtx_unlock(&sc->mfi_io_lock);
363059024Sobrien		}
363189857Sobrien
363289857Sobrien		return (error);
363389857Sobrien	case MFI_LINUX_SET_AEN_2: /* AEN Linux ioctl shim */
363489857Sobrien		error = copyin(arg, &l_aen, sizeof(l_aen));
3635130561Sobrien		if (error != 0)
3636130561Sobrien			return (error);
3637130561Sobrien		printf("AEN IMPLEMENTED for pid %d\n", curproc->p_pid);
363859024Sobrien		mfi_aen_entry = malloc(sizeof(struct mfi_aen), M_MFIBUF,
363960484Sobrien		    M_WAITOK);
364059024Sobrien		mtx_lock(&sc->mfi_io_lock);
364159024Sobrien		if (mfi_aen_entry != NULL) {
364259024Sobrien			mfi_aen_entry->p = curproc;
364359024Sobrien			TAILQ_INSERT_TAIL(&sc->mfi_aen_pids, mfi_aen_entry,
364459024Sobrien			    aen_link);
364559024Sobrien		}
364659024Sobrien		error = mfi_aen_register(sc, l_aen.laen_seq_num,
364760484Sobrien		    l_aen.laen_class_locale);
364860484Sobrien
364960484Sobrien		if (error != 0) {
365060484Sobrien			TAILQ_REMOVE(&sc->mfi_aen_pids, mfi_aen_entry,
365160484Sobrien			    aen_link);
365260484Sobrien			free(mfi_aen_entry, M_MFIBUF);
365360484Sobrien		}
365460484Sobrien		mtx_unlock(&sc->mfi_io_lock);
365577298Sobrien
365677298Sobrien		return (error);
365760484Sobrien	default:
365860484Sobrien		device_printf(sc->mfi_dev, "IOCTL 0x%lx not handled\n", cmd);
365960484Sobrien		error = ENOENT;
366060484Sobrien		break;
366160484Sobrien	}
366260484Sobrien
366359024Sobrien	return (error);
366459024Sobrien}
366577298Sobrien
366659024Sobrienstatic int
366759024Sobrienmfi_poll(struct cdev *dev, int poll_events, struct thread *td)
366859024Sobrien{
366977298Sobrien	struct mfi_softc *sc;
367059024Sobrien	int revents = 0;
367159024Sobrien
367277298Sobrien	sc = dev->si_drv1;
367359024Sobrien
367477298Sobrien	if (poll_events & (POLLIN | POLLRDNORM)) {
367577298Sobrien		if (sc->mfi_aen_triggered != 0) {
367659024Sobrien			revents |= poll_events & (POLLIN | POLLRDNORM);
367759024Sobrien			sc->mfi_aen_triggered = 0;
367877298Sobrien		}
367959024Sobrien		if (sc->mfi_aen_triggered == 0 && sc->mfi_aen_cm == NULL) {
368059024Sobrien			revents |= POLLERR;
368159024Sobrien		}
368259024Sobrien	}
368359024Sobrien
368459024Sobrien	if (revents == 0) {
368559024Sobrien		if (poll_events & (POLLIN | POLLRDNORM)) {
368659024Sobrien			sc->mfi_poll_waiting = 1;
368777298Sobrien			selrecord(td, &sc->mfi_select);
368877298Sobrien		}
368959024Sobrien	}
369059024Sobrien
369159024Sobrien	return revents;
369259024Sobrien}
369359024Sobrien
369459024Sobrienstatic void
369559024Sobrienmfi_dump_all(void)
369659024Sobrien{
369759024Sobrien	struct mfi_softc *sc;
369859024Sobrien	struct mfi_command *cm;
369959024Sobrien	devclass_t dc;
370077298Sobrien	time_t deadline;
370177298Sobrien	int timedout;
370259024Sobrien	int i;
370359024Sobrien
370459024Sobrien	dc = devclass_find("mfi");
370559024Sobrien	if (dc == NULL) {
370659024Sobrien		printf("No mfi dev class\n");
370759024Sobrien		return;
370859024Sobrien	}
370959024Sobrien
371060484Sobrien	for (i = 0; ; i++) {
371159024Sobrien		sc = devclass_get_softc(dc, i);
371259024Sobrien		if (sc == NULL)
371359024Sobrien			break;
371459024Sobrien		device_printf(sc->mfi_dev, "Dumping\n\n");
371560484Sobrien		timedout = 0;
371660484Sobrien		deadline = time_uptime - mfi_cmd_timeout;
371760484Sobrien		mtx_lock(&sc->mfi_io_lock);
371860484Sobrien		TAILQ_FOREACH(cm, &sc->mfi_busy, cm_link) {
3719218822Sdim			if (cm->cm_timestamp <= deadline) {
372060484Sobrien				device_printf(sc->mfi_dev,
372160484Sobrien				    "COMMAND %p TIMEOUT AFTER %d SECONDS\n",
372260484Sobrien				    cm, (int)(time_uptime - cm->cm_timestamp));
372360484Sobrien				MFI_PRINT_CMD(cm);
372460484Sobrien				timedout++;
372560484Sobrien			}
372660484Sobrien		}
372760484Sobrien
372860484Sobrien#if 0
372960484Sobrien		if (timedout)
373060484Sobrien			MFI_DUMP_CMDS(sc);
373160484Sobrien#endif
373260484Sobrien
373377298Sobrien		mtx_unlock(&sc->mfi_io_lock);
373459024Sobrien	}
373559024Sobrien
373659024Sobrien	return;
373759024Sobrien}
373859024Sobrien
373959024Sobrienstatic void
374059024Sobrienmfi_timeout(void *data)
374177298Sobrien{
374259024Sobrien	struct mfi_softc *sc = (struct mfi_softc *)data;
374359024Sobrien	struct mfi_command *cm, *tmp;
374459024Sobrien	time_t deadline;
374559024Sobrien	int timedout = 0;
374659024Sobrien
374759024Sobrien	deadline = time_uptime - mfi_cmd_timeout;
374859024Sobrien	if (sc->adpreset == 0) {
374959024Sobrien		if (!mfi_tbolt_reset(sc)) {
375059024Sobrien			callout_reset(&sc->mfi_watchdog_callout,
375159024Sobrien			    mfi_cmd_timeout * hz, mfi_timeout, sc);
375259024Sobrien			return;
375359024Sobrien		}
375459024Sobrien	}
375559024Sobrien	mtx_lock(&sc->mfi_io_lock);
375659024Sobrien	TAILQ_FOREACH_SAFE(cm, &sc->mfi_busy, cm_link, tmp) {
375759024Sobrien		if (sc->mfi_aen_cm == cm || sc->mfi_map_sync_cm == cm)
375859024Sobrien			continue;
375960484Sobrien		if (cm->cm_timestamp <= deadline) {
376059024Sobrien			if (sc->adpreset != 0 && sc->issuepend_done == 0) {
376159024Sobrien				cm->cm_timestamp = time_uptime;
376259024Sobrien			} else {
376359024Sobrien				device_printf(sc->mfi_dev,
376459024Sobrien				    "COMMAND %p TIMEOUT AFTER %d SECONDS\n",
376559024Sobrien				     cm, (int)(time_uptime - cm->cm_timestamp)
376659024Sobrien				     );
376759024Sobrien				MFI_PRINT_CMD(cm);
376860484Sobrien				MFI_VALIDATE_CMD(sc, cm);
376959024Sobrien				/*
377059024Sobrien				 * While commands can get stuck forever we do
377177298Sobrien				 * not fail them as there is no way to tell if
377259024Sobrien				 * the controller has actually processed them
377359024Sobrien				 * or not.
377459024Sobrien				 *
377559024Sobrien				 * In addition its very likely that force
377659024Sobrien				 * failing a command here would cause a panic
377759024Sobrien				 * e.g. in UFS.
377859024Sobrien				 */
377959024Sobrien				timedout++;
378060484Sobrien			}
378159024Sobrien		}
378259024Sobrien	}
378359024Sobrien
378459024Sobrien#if 0
378559024Sobrien	if (timedout)
378659024Sobrien		MFI_DUMP_CMDS(sc);
378759024Sobrien#endif
378859024Sobrien
378959024Sobrien	mtx_unlock(&sc->mfi_io_lock);
379059024Sobrien
379159024Sobrien	callout_reset(&sc->mfi_watchdog_callout, mfi_cmd_timeout * hz,
379259024Sobrien	    mfi_timeout, sc);
379359024Sobrien
379459024Sobrien	if (0)
379559024Sobrien		mfi_dump_all();
379659024Sobrien	return;
379760484Sobrien}
379860484Sobrien