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