diff options
author | ambrisko <ambrisko@FreeBSD.org> | 2012-03-30 23:05:48 +0000 |
---|---|---|
committer | ambrisko <ambrisko@FreeBSD.org> | 2012-03-30 23:05:48 +0000 |
commit | af288dfa91cb30ed7346d095af475eacf258fcee (patch) | |
tree | e1b865ec95e3c7d85bba7e6ba5c14bc2630a1809 /sys/dev/mfi | |
parent | 8251950705577f472f1a2c4052195c05c3e41d32 (diff) | |
parent | 096734a154b76780bd2761cf8e196c6fd4e0562d (diff) | |
download | FreeBSD-src-af288dfa91cb30ed7346d095af475eacf258fcee.zip FreeBSD-src-af288dfa91cb30ed7346d095af475eacf258fcee.tar.gz |
MFhead_mfi r227068
First cut of new HW support from LSI and merge into FreeBSD.
Supports Drake Skinny and ThunderBolt cards.
MFhead_mfi r227574
Style
MFhead_mfi r227579
Use bus_addr_t instead of uintXX_t.
MFhead_mfi r227580
MSI support
MFhead_mfi r227612
More bus_addr_t and remove "#ifdef __amd64__".
MFhead_mfi r227905
Improved timeout support from Scott.
MFhead_mfi r228108
Make file.
MFhead_mfi r228208
Fixed botched merge of Skinny support and enhanced handling
in call back routine.
MFhead_mfi r228279
Remove superfluous !TAILQ_EMPTY() checks before TAILQ_FOREACH().
MFhead_mfi r228310
Move mfi_decode_evt() to taskqueue.
MFhead_mfi r228320
Implement MFI_DEBUG for 64bit S/G lists.
MFhead_mfi r231988
Restore structure layout by reverting the array header to
use [0] instead of [1].
MFhead_mfi r232412
Put wildcard pattern later in the match table.
MFhead_mfi r232413
Use lower case for hexadecimal numbers to match surrounding
style.
MFhead_mfi r232414
Add more Thunderbolt variants.
MFhead_mfi r232888
Don't act on events prior to boot or when shutting down.
Add hw.mfi.detect_jbod_change to enable or disable acting
on JBOD type of disks being added on insert and removed on
removing. Switch hw.mfi.msi to 1 by default since it works
better on newer cards.
MFhead_mfi r233016
Release driver lock before taking Giant when deleting children.
Use TAILQ_FOREACH_SAFE when items can be deleted. Make code a
little simplier to follow. Fix a couple more style issues.
MFhead_mfi r233620
Update mfi_spare/mfi_array with the actual number of elements
for array_ref and pd. Change these max. #define names to avoid
name space collisions. This will require an update to mfiutil
It avoids mfiutil having to do a magic calculation.
Add a note and #define to state that a "SYSTEM" disk is really
what the firmware calls a "JBOD" drive.
Thanks to the many that helped, LSI for the initial code drop,
mav, delphij, jhb, sbruno that all helped with code and testing.
Diffstat (limited to 'sys/dev/mfi')
-rw-r--r-- | sys/dev/mfi/mfi.c | 1445 | ||||
-rw-r--r-- | sys/dev/mfi/mfi_cam.c | 6 | ||||
-rw-r--r-- | sys/dev/mfi/mfi_debug.c | 20 | ||||
-rw-r--r-- | sys/dev/mfi/mfi_disk.c | 21 | ||||
-rw-r--r-- | sys/dev/mfi/mfi_ioctl.h | 17 | ||||
-rw-r--r-- | sys/dev/mfi/mfi_linux.c | 1 | ||||
-rw-r--r-- | sys/dev/mfi/mfi_pci.c | 39 | ||||
-rw-r--r-- | sys/dev/mfi/mfi_syspd.c | 276 | ||||
-rw-r--r-- | sys/dev/mfi/mfi_tbolt.c | 1345 | ||||
-rw-r--r-- | sys/dev/mfi/mfireg.h | 659 | ||||
-rw-r--r-- | sys/dev/mfi/mfivar.h | 199 |
11 files changed, 3767 insertions, 261 deletions
diff --git a/sys/dev/mfi/mfi.c b/sys/dev/mfi/mfi.c index e55d7da..60ec822 100644 --- a/sys/dev/mfi/mfi.c +++ b/sys/dev/mfi/mfi.c @@ -53,6 +53,7 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_compat.h" #include "opt_mfi.h" #include <sys/param.h> @@ -72,6 +73,7 @@ __FBSDID("$FreeBSD$"); #include <sys/uio.h> #include <sys/proc.h> #include <sys/signalvar.h> +#include <sys/taskqueue.h> #include <machine/bus.h> #include <machine/resource.h> @@ -79,10 +81,11 @@ __FBSDID("$FreeBSD$"); #include <dev/mfi/mfireg.h> #include <dev/mfi/mfi_ioctl.h> #include <dev/mfi/mfivar.h> +#include <sys/interrupt.h> +#include <sys/priority.h> static int mfi_alloc_commands(struct mfi_softc *); static int mfi_comms_init(struct mfi_softc *); -static int mfi_wait_command(struct mfi_softc *, struct mfi_command *); static int mfi_get_controller_info(struct mfi_softc *); static int mfi_get_log_state(struct mfi_softc *, struct mfi_evt_log_state **); @@ -93,29 +96,39 @@ static void mfi_data_cb(void *, bus_dma_segment_t *, int, int); static void mfi_startup(void *arg); static void mfi_intr(void *arg); static void mfi_ldprobe(struct mfi_softc *sc); +static void mfi_syspdprobe(struct mfi_softc *sc); +static void mfi_handle_evt(void *context, int pending); static int mfi_aen_register(struct mfi_softc *sc, int seq, int locale); static void mfi_aen_complete(struct mfi_command *); -static int mfi_aen_setup(struct mfi_softc *, uint32_t); static int mfi_add_ld(struct mfi_softc *sc, int); static void mfi_add_ld_complete(struct mfi_command *); +static int mfi_add_sys_pd(struct mfi_softc *sc, int); +static void mfi_add_sys_pd_complete(struct mfi_command *); static struct mfi_command * mfi_bio_command(struct mfi_softc *); static void mfi_bio_complete(struct mfi_command *); -static int mfi_mapcmd(struct mfi_softc *, struct mfi_command *); +static struct mfi_command *mfi_build_ldio(struct mfi_softc *,struct bio*); +static struct mfi_command *mfi_build_syspdio(struct mfi_softc *,struct bio*); static int mfi_send_frame(struct mfi_softc *, struct mfi_command *); -static void mfi_complete(struct mfi_softc *, struct mfi_command *); static int mfi_abort(struct mfi_softc *, struct mfi_command *); static int mfi_linux_ioctl_int(struct cdev *, u_long, caddr_t, int, struct thread *); static void mfi_timeout(void *); static int mfi_user_command(struct mfi_softc *, struct mfi_ioc_passthru *); -static void mfi_enable_intr_xscale(struct mfi_softc *sc); -static void mfi_enable_intr_ppc(struct mfi_softc *sc); -static int32_t mfi_read_fw_status_xscale(struct mfi_softc *sc); -static int32_t mfi_read_fw_status_ppc(struct mfi_softc *sc); -static int mfi_check_clear_intr_xscale(struct mfi_softc *sc); -static int mfi_check_clear_intr_ppc(struct mfi_softc *sc); -static void mfi_issue_cmd_xscale(struct mfi_softc *sc,uint32_t bus_add,uint32_t frame_cnt); -static void mfi_issue_cmd_ppc(struct mfi_softc *sc,uint32_t bus_add,uint32_t frame_cnt); +static void mfi_enable_intr_xscale(struct mfi_softc *sc); +static void mfi_enable_intr_ppc(struct mfi_softc *sc); +static int32_t mfi_read_fw_status_xscale(struct mfi_softc *sc); +static int32_t mfi_read_fw_status_ppc(struct mfi_softc *sc); +static int mfi_check_clear_intr_xscale(struct mfi_softc *sc); +static int mfi_check_clear_intr_ppc(struct mfi_softc *sc); +static void mfi_issue_cmd_xscale(struct mfi_softc *sc, bus_addr_t bus_add, + uint32_t frame_cnt); +static void mfi_issue_cmd_ppc(struct mfi_softc *sc, bus_addr_t bus_add, + uint32_t frame_cnt); +static int mfi_config_lock(struct mfi_softc *sc, uint32_t opcode); +static void mfi_config_unlock(struct mfi_softc *sc, int locked); +static int mfi_check_command_pre(struct mfi_softc *sc, struct mfi_command *cm); +static void mfi_check_command_post(struct mfi_softc *sc, struct mfi_command *cm); +static int mfi_check_for_sscd(struct mfi_softc *sc, struct mfi_command *cm); SYSCTL_NODE(_hw, OID_AUTO, mfi, CTLFLAG_RD, 0, "MFI driver parameters"); static int mfi_event_locale = MFI_EVT_LOCALE_ALL; @@ -133,6 +146,11 @@ TUNABLE_INT("hw.mfi.max_cmds", &mfi_max_cmds); SYSCTL_INT(_hw_mfi, OID_AUTO, max_cmds, CTLFLAG_RD, &mfi_max_cmds, 0, "Max commands"); +static int mfi_detect_jbod_change = 1; +TUNABLE_INT("hw.mfi.detect_jbod_change", &mfi_detect_jbod_change); +SYSCTL_INT(_hw_mfi, OID_AUTO, detect_jbod_change, CTLFLAG_RW, + &mfi_detect_jbod_change, 0, "Detect a change to a JBOD"); + /* Management interface */ static d_open_t mfi_open; static d_close_t mfi_close; @@ -152,6 +170,7 @@ static struct cdevsw mfi_cdevsw = { MALLOC_DEFINE(M_MFIBUF, "mfibuf", "Buffers for the MFI driver"); #define MFI_INQ_LENGTH SHORT_INQUIRY_LENGTH +struct mfi_skinny_dma_info mfi_skinny; static void mfi_enable_intr_xscale(struct mfi_softc *sc) @@ -162,12 +181,17 @@ mfi_enable_intr_xscale(struct mfi_softc *sc) static void mfi_enable_intr_ppc(struct mfi_softc *sc) { - MFI_WRITE4(sc, MFI_ODCR0, 0xFFFFFFFF); if (sc->mfi_flags & MFI_FLAGS_1078) { + MFI_WRITE4(sc, MFI_ODCR0, 0xFFFFFFFF); MFI_WRITE4(sc, MFI_OMSK, ~MFI_1078_EIM); - } else if (sc->mfi_flags & MFI_FLAGS_GEN2) { + } + else if (sc->mfi_flags & MFI_FLAGS_GEN2) { + MFI_WRITE4(sc, MFI_ODCR0, 0xFFFFFFFF); MFI_WRITE4(sc, MFI_OMSK, ~MFI_GEN2_EIM); } + else if (sc->mfi_flags & MFI_FLAGS_SKINNY) { + MFI_WRITE4(sc, MFI_OMSK, ~0x00000001); + } } static int32_t @@ -205,35 +229,51 @@ mfi_check_clear_intr_ppc(struct mfi_softc *sc) if (!(status & MFI_1078_RM)) { return 1; } - } else if (sc->mfi_flags & MFI_FLAGS_GEN2) { + } + else if (sc->mfi_flags & MFI_FLAGS_GEN2) { if (!(status & MFI_GEN2_RM)) { return 1; } } - - MFI_WRITE4(sc, MFI_ODCR0, status); + else if (sc->mfi_flags & MFI_FLAGS_SKINNY) { + if (!(status & MFI_SKINNY_RM)) { + return 1; + } + } + if (sc->mfi_flags & MFI_FLAGS_SKINNY) + MFI_WRITE4(sc, MFI_OSTS, status); + else + MFI_WRITE4(sc, MFI_ODCR0, status); return 0; } static void -mfi_issue_cmd_xscale(struct mfi_softc *sc,uint32_t bus_add,uint32_t frame_cnt) +mfi_issue_cmd_xscale(struct mfi_softc *sc, bus_addr_t bus_add, uint32_t frame_cnt) { MFI_WRITE4(sc, MFI_IQP,(bus_add >>3)|frame_cnt); } static void -mfi_issue_cmd_ppc(struct mfi_softc *sc,uint32_t bus_add,uint32_t frame_cnt) +mfi_issue_cmd_ppc(struct mfi_softc *sc, bus_addr_t bus_add, uint32_t frame_cnt) { - MFI_WRITE4(sc, MFI_IQP, (bus_add |frame_cnt <<1)|1 ); + if (sc->mfi_flags & MFI_FLAGS_SKINNY) { + MFI_WRITE4(sc, MFI_IQPL, (bus_add | frame_cnt <<1)|1 ); + MFI_WRITE4(sc, MFI_IQPH, 0x00000000); + } else { + MFI_WRITE4(sc, MFI_IQP, (bus_add | frame_cnt <<1)|1 ); + } } -static int +int mfi_transition_firmware(struct mfi_softc *sc) { uint32_t fw_state, cur_state; int max_wait, i; + uint32_t cur_abs_reg_val = 0; + uint32_t prev_abs_reg_val = 0; - fw_state = sc->mfi_read_fw_status(sc)& MFI_FWSTATE_MASK; + cur_abs_reg_val = sc->mfi_read_fw_status(sc); + fw_state = cur_abs_reg_val & MFI_FWSTATE_MASK; while (fw_state != MFI_FWSTATE_READY) { if (bootverbose) device_printf(sc->mfi_dev, "Waiting for firmware to " @@ -244,38 +284,60 @@ mfi_transition_firmware(struct mfi_softc *sc) device_printf(sc->mfi_dev, "Firmware fault\n"); return (ENXIO); case MFI_FWSTATE_WAIT_HANDSHAKE: - MFI_WRITE4(sc, MFI_IDB, MFI_FWINIT_CLEAR_HANDSHAKE); - max_wait = 2; + if (sc->mfi_flags & MFI_FLAGS_SKINNY || sc->mfi_flags & MFI_FLAGS_TBOLT) + MFI_WRITE4(sc, MFI_SKINNY_IDB, MFI_FWINIT_CLEAR_HANDSHAKE); + else + MFI_WRITE4(sc, MFI_IDB, MFI_FWINIT_CLEAR_HANDSHAKE); + max_wait = MFI_RESET_WAIT_TIME; break; case MFI_FWSTATE_OPERATIONAL: - MFI_WRITE4(sc, MFI_IDB, MFI_FWINIT_READY); - max_wait = 10; + if (sc->mfi_flags & MFI_FLAGS_SKINNY || sc->mfi_flags & MFI_FLAGS_TBOLT) + MFI_WRITE4(sc, MFI_SKINNY_IDB, 7); + else + MFI_WRITE4(sc, MFI_IDB, MFI_FWINIT_READY); + max_wait = MFI_RESET_WAIT_TIME; break; case MFI_FWSTATE_UNDEFINED: case MFI_FWSTATE_BB_INIT: - max_wait = 2; + max_wait = MFI_RESET_WAIT_TIME; + break; + case MFI_FWSTATE_FW_INIT_2: + max_wait = MFI_RESET_WAIT_TIME; break; case MFI_FWSTATE_FW_INIT: - case MFI_FWSTATE_DEVICE_SCAN: case MFI_FWSTATE_FLUSH_CACHE: - max_wait = 20; + max_wait = MFI_RESET_WAIT_TIME; + break; + case MFI_FWSTATE_DEVICE_SCAN: + max_wait = MFI_RESET_WAIT_TIME; /* wait for 180 seconds */ + prev_abs_reg_val = cur_abs_reg_val; break; case MFI_FWSTATE_BOOT_MESSAGE_PENDING: - MFI_WRITE4(sc, MFI_IDB, MFI_FWINIT_HOTPLUG); - max_wait = 10; + if (sc->mfi_flags & MFI_FLAGS_SKINNY || sc->mfi_flags & MFI_FLAGS_TBOLT) + MFI_WRITE4(sc, MFI_SKINNY_IDB, MFI_FWINIT_HOTPLUG); + else + MFI_WRITE4(sc, MFI_IDB, MFI_FWINIT_HOTPLUG); + max_wait = MFI_RESET_WAIT_TIME; break; default: - device_printf(sc->mfi_dev,"Unknown firmware state %#x\n", + device_printf(sc->mfi_dev, "Unknown firmware state %#x\n", fw_state); return (ENXIO); } for (i = 0; i < (max_wait * 10); i++) { - fw_state = sc->mfi_read_fw_status(sc) & MFI_FWSTATE_MASK; + cur_abs_reg_val = sc->mfi_read_fw_status(sc); + fw_state = cur_abs_reg_val & MFI_FWSTATE_MASK; if (fw_state == cur_state) DELAY(100000); else break; } + if (fw_state == MFI_FWSTATE_DEVICE_SCAN) { + /* Check the device scanning progress */ + if (prev_abs_reg_val != cur_abs_reg_val) { + continue; + } + } if (fw_state == cur_state) { device_printf(sc->mfi_dev, "Firmware stuck in state " "%#x\n", fw_state); @@ -286,26 +348,35 @@ mfi_transition_firmware(struct mfi_softc *sc) } static void -mfi_addr32_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) +mfi_addr_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { - uint32_t *addr; + bus_addr_t *addr; addr = arg; *addr = segs[0].ds_addr; } + int mfi_attach(struct mfi_softc *sc) { uint32_t status; int error, commsz, framessz, sensesz; int frames, unit, max_fw_sge; + uint32_t tb_mem_size = 0; - device_printf(sc->mfi_dev, "Megaraid SAS driver Ver 3.00 \n"); + if (sc == NULL) + return EINVAL; + + device_printf(sc->mfi_dev, "Megaraid SAS driver Ver %s \n", + MEGASAS_VERSION); mtx_init(&sc->mfi_io_lock, "MFI I/O lock", NULL, MTX_DEF); sx_init(&sc->mfi_config_lock, "MFI config"); TAILQ_INIT(&sc->mfi_ld_tqh); + TAILQ_INIT(&sc->mfi_syspd_tqh); + TAILQ_INIT(&sc->mfi_evt_queue); + TASK_INIT(&sc->mfi_evt_task, 0, mfi_handle_evt, sc); TAILQ_INIT(&sc->mfi_aen_pids); TAILQ_INIT(&sc->mfi_cam_ccbq); @@ -314,15 +385,29 @@ mfi_attach(struct mfi_softc *sc) mfi_initq_busy(sc); mfi_initq_bio(sc); + sc->adpreset = 0; + sc->last_seq_num = 0; + sc->disableOnlineCtrlReset = 1; + sc->issuepend_done = 1; + sc->hw_crit_error = 0; + if (sc->mfi_flags & MFI_FLAGS_1064R) { sc->mfi_enable_intr = mfi_enable_intr_xscale; sc->mfi_read_fw_status = mfi_read_fw_status_xscale; sc->mfi_check_clear_intr = mfi_check_clear_intr_xscale; sc->mfi_issue_cmd = mfi_issue_cmd_xscale; - } - else { + } else if (sc->mfi_flags & MFI_FLAGS_TBOLT) { + sc->mfi_enable_intr = mfi_tbolt_enable_intr_ppc; + sc->mfi_disable_intr = mfi_tbolt_disable_intr_ppc; + sc->mfi_read_fw_status = mfi_tbolt_read_fw_status_ppc; + sc->mfi_check_clear_intr = mfi_tbolt_check_clear_intr_ppc; + sc->mfi_issue_cmd = mfi_tbolt_issue_cmd_ppc; + sc->mfi_adp_reset = mfi_tbolt_adp_reset; + sc->mfi_tbolt = 1; + TAILQ_INIT(&sc->mfi_cmd_tbolt_tqh); + } else { sc->mfi_enable_intr = mfi_enable_intr_ppc; - sc->mfi_read_fw_status = mfi_read_fw_status_ppc; + sc->mfi_read_fw_status = mfi_read_fw_status_ppc; sc->mfi_check_clear_intr = mfi_check_clear_intr_ppc; sc->mfi_issue_cmd = mfi_issue_cmd_ppc; } @@ -335,6 +420,32 @@ mfi_attach(struct mfi_softc *sc) return (ENXIO); } + /* Start: LSIP200113393 */ + if (bus_dma_tag_create( sc->mfi_parent_dmat, /* parent */ + 1, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MEGASAS_MAX_NAME*sizeof(bus_addr_t), /* maxsize */ + 1, /* msegments */ + MEGASAS_MAX_NAME*sizeof(bus_addr_t), /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->verbuf_h_dmat)) { + device_printf(sc->mfi_dev, "Cannot allocate verbuf_h_dmat DMA tag\n"); + return (ENOMEM); + } + if (bus_dmamem_alloc(sc->verbuf_h_dmat, (void **)&sc->verbuf, + BUS_DMA_NOWAIT, &sc->verbuf_h_dmamap)) { + device_printf(sc->mfi_dev, "Cannot allocate verbuf_h_dmamap memory\n"); + return (ENOMEM); + } + bzero(sc->verbuf, MEGASAS_MAX_NAME*sizeof(bus_addr_t)); + bus_dmamap_load(sc->verbuf_h_dmat, sc->verbuf_h_dmamap, + sc->verbuf, MEGASAS_MAX_NAME*sizeof(bus_addr_t), + mfi_addr_cb, &sc->verbuf_h_busaddr, 0); + /* End: LSIP200113393 */ + /* * Get information needed for sizing the contiguous memory for the * frame pool. Size down the sgl parameter since we know that @@ -347,6 +458,100 @@ mfi_attach(struct mfi_softc *sc) max_fw_sge = (status & MFI_FWSTATE_MAXSGL_MASK) >> 16; sc->mfi_max_sge = min(max_fw_sge, ((MFI_MAXPHYS / PAGE_SIZE) + 1)); + /* ThunderBolt Support get the contiguous memory */ + + if (sc->mfi_flags & MFI_FLAGS_TBOLT) { + mfi_tbolt_init_globals(sc); + device_printf(sc->mfi_dev, "MaxCmd = %x MaxSgl = %x state = %x \n", + sc->mfi_max_fw_cmds, sc->mfi_max_sge, status); + tb_mem_size = mfi_tbolt_get_memory_requirement(sc); + + if (bus_dma_tag_create( sc->mfi_parent_dmat, /* parent */ + 1, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + tb_mem_size, /* maxsize */ + 1, /* msegments */ + tb_mem_size, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->mfi_tb_dmat)) { + device_printf(sc->mfi_dev, "Cannot allocate comms DMA tag\n"); + return (ENOMEM); + } + if (bus_dmamem_alloc(sc->mfi_tb_dmat, (void **)&sc->request_message_pool, + BUS_DMA_NOWAIT, &sc->mfi_tb_dmamap)) { + device_printf(sc->mfi_dev, "Cannot allocate comms memory\n"); + return (ENOMEM); + } + bzero(sc->request_message_pool, tb_mem_size); + bus_dmamap_load(sc->mfi_tb_dmat, sc->mfi_tb_dmamap, + sc->request_message_pool, tb_mem_size, mfi_addr_cb, &sc->mfi_tb_busaddr, 0); + + /* For ThunderBolt memory init */ + if (bus_dma_tag_create( sc->mfi_parent_dmat, /* parent */ + 0x100, 0, /* alignmnt, boundary */ + BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MFI_FRAME_SIZE, /* maxsize */ + 1, /* msegments */ + MFI_FRAME_SIZE, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->mfi_tb_init_dmat)) { + device_printf(sc->mfi_dev, "Cannot allocate init DMA tag\n"); + return (ENOMEM); + } + if (bus_dmamem_alloc(sc->mfi_tb_init_dmat, (void **)&sc->mfi_tb_init, + BUS_DMA_NOWAIT, &sc->mfi_tb_init_dmamap)) { + device_printf(sc->mfi_dev, "Cannot allocate init memory\n"); + return (ENOMEM); + } + bzero(sc->mfi_tb_init, MFI_FRAME_SIZE); + bus_dmamap_load(sc->mfi_tb_init_dmat, sc->mfi_tb_init_dmamap, + sc->mfi_tb_init, MFI_FRAME_SIZE, mfi_addr_cb, + &sc->mfi_tb_init_busaddr, 0); + if (mfi_tbolt_init_desc_pool(sc, sc->request_message_pool, + tb_mem_size)) { + device_printf(sc->mfi_dev, + "Thunderbolt pool preparation error\n"); + return 0; + } + + /* + Allocate DMA memory mapping for MPI2 IOC Init descriptor, + we are taking it diffrent from what we have allocated for Request + and reply descriptors to avoid confusion later + */ + tb_mem_size = sizeof(struct MPI2_IOC_INIT_REQUEST); + if (bus_dma_tag_create( sc->mfi_parent_dmat, /* parent */ + 1, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + tb_mem_size, /* maxsize */ + 1, /* msegments */ + tb_mem_size, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->mfi_tb_ioc_init_dmat)) { + device_printf(sc->mfi_dev, + "Cannot allocate comms DMA tag\n"); + return (ENOMEM); + } + if (bus_dmamem_alloc(sc->mfi_tb_ioc_init_dmat, + (void **)&sc->mfi_tb_ioc_init_desc, + BUS_DMA_NOWAIT, &sc->mfi_tb_ioc_init_dmamap)) { + device_printf(sc->mfi_dev, "Cannot allocate comms memory\n"); + return (ENOMEM); + } + bzero(sc->mfi_tb_ioc_init_desc, tb_mem_size); + bus_dmamap_load(sc->mfi_tb_ioc_init_dmat, sc->mfi_tb_ioc_init_dmamap, + sc->mfi_tb_ioc_init_desc, tb_mem_size, mfi_addr_cb, + &sc->mfi_tb_ioc_init_busaddr, 0); + } /* * Create the dma tag for data buffers. Used both for block I/O * and for various internal data queries. @@ -396,8 +601,7 @@ mfi_attach(struct mfi_softc *sc) } bzero(sc->mfi_comms, commsz); bus_dmamap_load(sc->mfi_comms_dmat, sc->mfi_comms_dmamap, - sc->mfi_comms, commsz, mfi_addr32_cb, &sc->mfi_comms_busaddr, 0); - + sc->mfi_comms, commsz, mfi_addr_cb, &sc->mfi_comms_busaddr, 0); /* * Allocate DMA memory for the command frames. Keep them in the * lower 4GB for efficiency. Calculate the size of the commands at @@ -414,6 +618,8 @@ mfi_attach(struct mfi_softc *sc) } else { sc->mfi_sge_size = sizeof(struct mfi_sg32); } + if (sc->mfi_flags & MFI_FLAGS_SKINNY) + sc->mfi_sge_size = sizeof(struct mfi_sg_skinny); frames = (sc->mfi_sge_size * sc->mfi_max_sge - 1) / MFI_FRAME_SIZE + 2; sc->mfi_cmd_size = frames * MFI_FRAME_SIZE; framessz = sc->mfi_cmd_size * sc->mfi_max_fw_cmds; @@ -438,8 +644,7 @@ mfi_attach(struct mfi_softc *sc) } bzero(sc->mfi_frames, framessz); bus_dmamap_load(sc->mfi_frames_dmat, sc->mfi_frames_dmamap, - sc->mfi_frames, framessz, mfi_addr32_cb, &sc->mfi_frames_busaddr,0); - + sc->mfi_frames, framessz, mfi_addr_cb, &sc->mfi_frames_busaddr,0); /* * Allocate DMA memory for the frame sense data. Keep them in the * lower 4GB for efficiency @@ -465,32 +670,47 @@ mfi_attach(struct mfi_softc *sc) return (ENOMEM); } bus_dmamap_load(sc->mfi_sense_dmat, sc->mfi_sense_dmamap, - sc->mfi_sense, sensesz, mfi_addr32_cb, &sc->mfi_sense_busaddr, 0); - + sc->mfi_sense, sensesz, mfi_addr_cb, &sc->mfi_sense_busaddr, 0); if ((error = mfi_alloc_commands(sc)) != 0) return (error); - if ((error = mfi_comms_init(sc)) != 0) - return (error); + /* Before moving the FW to operational state, check whether + * hostmemory is required by the FW or not + */ - if ((error = mfi_get_controller_info(sc)) != 0) - return (error); + /* ThunderBolt MFI_IOC2 INIT */ + if (sc->mfi_flags & MFI_FLAGS_TBOLT) { + sc->mfi_disable_intr(sc); + if ((error = mfi_tbolt_init_MFI_queue(sc)) != 0) { + device_printf(sc->mfi_dev, + "TB Init has failed with error %d\n",error); + return error; + } - mtx_lock(&sc->mfi_io_lock); - if ((error = mfi_aen_setup(sc, 0), 0) != 0) { - mtx_unlock(&sc->mfi_io_lock); - return (error); - } - mtx_unlock(&sc->mfi_io_lock); + if ((error = mfi_tbolt_alloc_cmd(sc)) != 0) + return error; + if (bus_setup_intr(sc->mfi_dev, sc->mfi_irq, + INTR_MPSAFE|INTR_TYPE_BIO, NULL, mfi_intr_tbolt, sc, + &sc->mfi_intr)) { + device_printf(sc->mfi_dev, "Cannot set up interrupt\n"); + return (EINVAL); + } + sc->mfi_enable_intr(sc); + sc->map_id = 0; + } else { + if ((error = mfi_comms_init(sc)) != 0) + return (error); - /* - * Set up the interrupt handler. - */ - if (bus_setup_intr(sc->mfi_dev, sc->mfi_irq, INTR_MPSAFE|INTR_TYPE_BIO, - NULL, mfi_intr, sc, &sc->mfi_intr)) { - device_printf(sc->mfi_dev, "Cannot set up interrupt\n"); - return (EINVAL); + if (bus_setup_intr(sc->mfi_dev, sc->mfi_irq, + INTR_MPSAFE|INTR_TYPE_BIO, NULL, mfi_intr, sc, &sc->mfi_intr)) { + device_printf(sc->mfi_dev, "Cannot set up interrupt\n"); + return (EINVAL); + } + sc->mfi_enable_intr(sc); } + if ((error = mfi_get_controller_info(sc)) != 0) + return (error); + sc->disableOnlineCtrlReset = 0; /* Register a config hook to probe the bus for arrays */ sc->mfi_ich.ich_func = mfi_startup; @@ -500,6 +720,10 @@ mfi_attach(struct mfi_softc *sc) "hook\n"); return (EINVAL); } + if ((error = mfi_aen_setup(sc, 0), 0) != 0) { + mtx_unlock(&sc->mfi_io_lock); + return (error); + } /* * Register a shutdown handler. @@ -571,8 +795,11 @@ mfi_alloc_commands(struct mfi_softc *sc) cm->cm_sc = sc; cm->cm_index = i; if (bus_dmamap_create(sc->mfi_buffer_dmat, 0, - &cm->cm_dmamap) == 0) + &cm->cm_dmamap) == 0) { + mtx_lock(&sc->mfi_io_lock); mfi_release_command(cm); + mtx_unlock(&sc->mfi_io_lock); + } else break; sc->mfi_total_cmds++; @@ -587,6 +814,8 @@ mfi_release_command(struct mfi_command *cm) struct mfi_frame_header *hdr; uint32_t *hdr_data; + mtx_assert(&cm->cm_sc->mfi_io_lock, MA_OWNED); + /* * Zero out the important fields of the frame, but make sure the * context field is preserved. For efficiency, handle the fields @@ -611,24 +840,31 @@ mfi_release_command(struct mfi_command *cm) cm->cm_data = NULL; cm->cm_sg = 0; cm->cm_total_frame_size = 0; + cm->retry_for_fw_reset = 0; mfi_enqueue_free(cm); } static int -mfi_dcmd_command(struct mfi_softc *sc, struct mfi_command **cmp, uint32_t opcode, - void **bufp, size_t bufsize) +mfi_dcmd_command(struct mfi_softc *sc, struct mfi_command **cmp, + uint32_t opcode, void **bufp, size_t bufsize) { struct mfi_command *cm; struct mfi_dcmd_frame *dcmd; void *buf = NULL; - + uint32_t context = 0; + mtx_assert(&sc->mfi_io_lock, MA_OWNED); - + cm = mfi_dequeue_free(sc); if (cm == NULL) return (EBUSY); + /* Zero out the MFI frame */ + context = cm->cm_frame->header.context; + bzero(cm->cm_frame, sizeof(union mfi_frame)); + cm->cm_frame->header.context = context; + if ((bufsize > 0) && (bufp != NULL)) { if (*bufp == NULL) { buf = malloc(bufsize, M_MFIBUF, M_NOWAIT|M_ZERO); @@ -648,6 +884,7 @@ mfi_dcmd_command(struct mfi_softc *sc, struct mfi_command **cmp, uint32_t opcode dcmd->header.timeout = 0; dcmd->header.flags = 0; dcmd->header.data_len = bufsize; + dcmd->header.scsi_status = 0; dcmd->opcode = opcode; cm->cm_sg = &dcmd->sgl; cm->cm_total_frame_size = MFI_DCMD_FRAME_SIZE; @@ -669,11 +906,17 @@ mfi_comms_init(struct mfi_softc *sc) struct mfi_init_frame *init; struct mfi_init_qinfo *qinfo; int error; + uint32_t context = 0; mtx_lock(&sc->mfi_io_lock); if ((cm = mfi_dequeue_free(sc)) == NULL) return (EBUSY); + /* Zero out the MFI frame */ + context = cm->cm_frame->header.context; + bzero(cm->cm_frame, sizeof(union mfi_frame)); + cm->cm_frame->header.context = context; + /* * Abuse the SG list area of the frame to hold the init_qinfo * object; @@ -734,9 +977,11 @@ mfi_get_controller_info(struct mfi_softc *sc) BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap); - max_sectors_1 = (1 << ci->stripe_sz_ops.min) * ci->max_strips_per_io; + max_sectors_1 = (1 << ci->stripe_sz_ops.max) * ci->max_strips_per_io; max_sectors_2 = ci->max_request_size; sc->mfi_max_io = min(max_sectors_1, max_sectors_2); + sc->disableOnlineCtrlReset = + ci->properties.OnOffProperties.disableOnlineCtrlReset; out: if (ci) @@ -753,6 +998,7 @@ mfi_get_log_state(struct mfi_softc *sc, struct mfi_evt_log_state **log_state) struct mfi_command *cm = NULL; int error; + mtx_lock(&sc->mfi_io_lock); error = mfi_dcmd_command(sc, &cm, MFI_DCMD_CTRL_EVENT_GETINFO, (void **)log_state, sizeof(**log_state)); if (error) @@ -771,11 +1017,12 @@ mfi_get_log_state(struct mfi_softc *sc, struct mfi_evt_log_state **log_state) out: if (cm) mfi_release_command(cm); + mtx_unlock(&sc->mfi_io_lock); return (error); } -static int +int mfi_aen_setup(struct mfi_softc *sc, uint32_t seq_start) { struct mfi_evt_log_state *log_state = NULL; @@ -789,6 +1036,7 @@ mfi_aen_setup(struct mfi_softc *sc, uint32_t seq_start) if (seq_start == 0) { error = mfi_get_log_state(sc, &log_state); + sc->mfi_boot_seq_num = log_state->boot_seq_num; if (error) { if (log_state) free(log_state, M_MFIBUF); @@ -810,7 +1058,7 @@ mfi_aen_setup(struct mfi_softc *sc, uint32_t seq_start) return 0; } -static int +int mfi_wait_command(struct mfi_softc *sc, struct mfi_command *cm) { @@ -883,6 +1131,64 @@ mfi_free(struct mfi_softc *sc) if (sc->mfi_comms_dmat != NULL) bus_dma_tag_destroy(sc->mfi_comms_dmat); + /* ThunderBolt contiguous memory free here */ + if (sc->mfi_flags & MFI_FLAGS_TBOLT) { + if (sc->mfi_tb_busaddr != 0) + bus_dmamap_unload(sc->mfi_tb_dmat, sc->mfi_tb_dmamap); + if (sc->request_message_pool != NULL) + bus_dmamem_free(sc->mfi_tb_dmat, sc->request_message_pool, + sc->mfi_tb_dmamap); + if (sc->mfi_tb_dmat != NULL) + bus_dma_tag_destroy(sc->mfi_tb_dmat); + + /* Version buffer memory free */ + /* Start LSIP200113393 */ + if (sc->verbuf_h_busaddr != 0) + bus_dmamap_unload(sc->verbuf_h_dmat, sc->verbuf_h_dmamap); + if (sc->verbuf != NULL) + bus_dmamem_free(sc->verbuf_h_dmat, sc->verbuf, + sc->verbuf_h_dmamap); + if (sc->verbuf_h_dmat != NULL) + bus_dma_tag_destroy(sc->verbuf_h_dmat); + + /* End LSIP200113393 */ + /* ThunderBolt INIT packet memory Free */ + if (sc->mfi_tb_init_busaddr != 0) + bus_dmamap_unload(sc->mfi_tb_init_dmat, sc->mfi_tb_init_dmamap); + if (sc->mfi_tb_init != NULL) + bus_dmamem_free(sc->mfi_tb_init_dmat, sc->mfi_tb_init, + sc->mfi_tb_init_dmamap); + if (sc->mfi_tb_init_dmat != NULL) + bus_dma_tag_destroy(sc->mfi_tb_init_dmat); + + /* ThunderBolt IOC Init Desc memory free here */ + if (sc->mfi_tb_ioc_init_busaddr != 0) + bus_dmamap_unload(sc->mfi_tb_ioc_init_dmat, + sc->mfi_tb_ioc_init_dmamap); + if (sc->mfi_tb_ioc_init_desc != NULL) + bus_dmamem_free(sc->mfi_tb_ioc_init_dmat, + sc->mfi_tb_ioc_init_desc, + sc->mfi_tb_ioc_init_dmamap); + if (sc->mfi_tb_ioc_init_dmat != NULL) + bus_dma_tag_destroy(sc->mfi_tb_ioc_init_dmat); + for (int i = 0; i < sc->mfi_max_fw_cmds; i++) { + if (sc->mfi_cmd_pool_tbolt != NULL) { + if (sc->mfi_cmd_pool_tbolt[i] != NULL) { + free(sc->mfi_cmd_pool_tbolt[i], + M_MFIBUF); + sc->mfi_cmd_pool_tbolt[i] = NULL; + } + } + } + if (sc->mfi_cmd_pool_tbolt != NULL) { + free(sc->mfi_cmd_pool_tbolt, M_MFIBUF); + sc->mfi_cmd_pool_tbolt = NULL; + } + if (sc->request_desc_pool != NULL) { + free(sc->request_desc_pool, M_MFIBUF); + sc->request_desc_pool = NULL; + } + } if (sc->mfi_buffer_dmat != NULL) bus_dma_tag_destroy(sc->mfi_buffer_dmat); if (sc->mfi_parent_dmat != NULL) @@ -909,6 +1215,8 @@ mfi_startup(void *arg) sx_xlock(&sc->mfi_config_lock); mtx_lock(&sc->mfi_io_lock); mfi_ldprobe(sc); + if (sc->mfi_flags & MFI_FLAGS_SKINNY) + mfi_syspdprobe(sc); mtx_unlock(&sc->mfi_io_lock); sx_xunlock(&sc->mfi_config_lock); } @@ -925,12 +1233,7 @@ mfi_intr(void *arg) if (sc->mfi_check_clear_intr(sc)) return; - /* - * Do a dummy read to flush the interrupt ACK that we just performed, - * ensuring that everything is really, truly consistent. - */ - (void)sc->mfi_read_fw_status(sc); - +restart: pi = sc->mfi_comms->hw_pi; ci = sc->mfi_comms->hw_ci; mtx_lock(&sc->mfi_io_lock); @@ -955,6 +1258,14 @@ mfi_intr(void *arg) mfi_startio(sc); mtx_unlock(&sc->mfi_io_lock); + /* + * Dummy read to flush the bus; this ensures that the indexes are up + * to date. Restart processing if more commands have come it. + */ + (void)sc->mfi_read_fw_status(sc); + if (pi != sc->mfi_comms->hw_pi) + goto restart; + return; } @@ -975,6 +1286,9 @@ mfi_shutdown(struct mfi_softc *sc) if (sc->mfi_aen_cm != NULL) mfi_abort(sc, sc->mfi_aen_cm); + if (sc->map_update_cmd != NULL) + mfi_abort(sc, sc->map_update_cmd); + dcmd = &cm->cm_frame->dcmd; dcmd->header.flags = MFI_FRAME_DIR_NONE; cm->cm_flags = MFI_CMD_POLLED; @@ -990,6 +1304,81 @@ mfi_shutdown(struct mfi_softc *sc) } static void +mfi_syspdprobe(struct mfi_softc *sc) +{ + struct mfi_frame_header *hdr; + struct mfi_command *cm = NULL; + struct mfi_pd_list *pdlist = NULL; + struct mfi_system_pd *syspd, *tmp; + int error, i, found; + + sx_assert(&sc->mfi_config_lock, SA_XLOCKED); + mtx_assert(&sc->mfi_io_lock, MA_OWNED); + /* Add SYSTEM PD's */ + error = mfi_dcmd_command(sc, &cm, MFI_DCMD_PD_LIST_QUERY, + (void **)&pdlist, sizeof(*pdlist)); + if (error){ + device_printf(sc->mfi_dev, + "Error while forming SYSTEM PD list\n"); + goto out; + } + + cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_POLLED; + cm->cm_frame->dcmd.mbox[0] = MR_PD_QUERY_TYPE_EXPOSED_TO_HOST; + cm->cm_frame->dcmd.mbox[1] = 0; + if (mfi_mapcmd(sc, cm) != 0) { + device_printf(sc->mfi_dev, + "Failed to get syspd device listing\n"); + goto out; + } + bus_dmamap_sync(sc->mfi_buffer_dmat,cm->cm_dmamap, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap); + hdr = &cm->cm_frame->header; + if (hdr->cmd_status != MFI_STAT_OK) { + device_printf(sc->mfi_dev, + "MFI_DCMD_PD_LIST_QUERY failed %x\n", hdr->cmd_status); + goto out; + } + /* Get each PD and add it to the system */ + for (i = 0; i < pdlist->count; i++) { + if (pdlist->addr[i].device_id == + pdlist->addr[i].encl_device_id) + continue; + found = 0; + TAILQ_FOREACH(syspd, &sc->mfi_syspd_tqh, pd_link) { + if (syspd->pd_id == pdlist->addr[i].device_id) + found = 1; + } + if (found == 0) + mfi_add_sys_pd(sc, pdlist->addr[i].device_id); + } + /* Delete SYSPD's whose state has been changed */ + TAILQ_FOREACH_SAFE(syspd, &sc->mfi_syspd_tqh, pd_link, tmp) { + found = 0; + for (i = 0; i < pdlist->count; i++) { + if (syspd->pd_id == pdlist->addr[i].device_id) + found = 1; + } + if (found == 0) { + printf("DELETE\n"); + mtx_unlock(&sc->mfi_io_lock); + mtx_lock(&Giant); + device_delete_child(sc->mfi_dev, syspd->pd_dev); + mtx_unlock(&Giant); + mtx_lock(&sc->mfi_io_lock); + } + } +out: + if (pdlist) + free(pdlist, M_MFIBUF); + if (cm) + mfi_release_command(cm); + + return; +} + +static void mfi_ldprobe(struct mfi_softc *sc) { struct mfi_frame_header *hdr; @@ -1083,10 +1472,124 @@ format_class(int8_t class) static void mfi_decode_evt(struct mfi_softc *sc, struct mfi_evt_detail *detail) { + struct mfi_system_pd *syspd = NULL; device_printf(sc->mfi_dev, "%d (%s/0x%04x/%s) - %s\n", detail->seq, format_timestamp(detail->time), detail->evt_class.members.locale, - format_class(detail->evt_class.members.evt_class), detail->description); + format_class(detail->evt_class.members.evt_class), + detail->description); + + /* Don't act on old AEN's or while shutting down */ + if (detail->seq < sc->mfi_boot_seq_num || sc->mfi_detaching) + return; + + switch (detail->arg_type) { + case MR_EVT_ARGS_NONE: + if (detail->code == MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED) { + device_printf(sc->mfi_dev, "HostBus scan raised\n"); + if (mfi_detect_jbod_change) { + /* + * Probe for new SYSPD's and Delete + * invalid SYSPD's + */ + sx_xlock(&sc->mfi_config_lock); + mtx_lock(&sc->mfi_io_lock); + mfi_syspdprobe(sc); + mtx_unlock(&sc->mfi_io_lock); + sx_xunlock(&sc->mfi_config_lock); + } + } + break; + case MR_EVT_ARGS_LD_STATE: + /* During load time driver reads all the events starting + * from the one that has been logged after shutdown. Avoid + * these old events. + */ + if (detail->args.ld_state.new_state == MFI_LD_STATE_OFFLINE ) { + /* Remove the LD */ + struct mfi_disk *ld; + TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link) { + if (ld->ld_id == + detail->args.ld_state.ld.target_id) + break; + } + /* + Fix: for kernel panics when SSCD is removed + KASSERT(ld != NULL, ("volume dissappeared")); + */ + if (ld != NULL) { + mtx_lock(&Giant); + device_delete_child(sc->mfi_dev, ld->ld_dev); + mtx_unlock(&Giant); + } + } + break; + case MR_EVT_ARGS_PD: + if (detail->code == MR_EVT_PD_REMOVED) { + if (mfi_detect_jbod_change) { + /* + * If the removed device is a SYSPD then + * delete it + */ + TAILQ_FOREACH(syspd, &sc->mfi_syspd_tqh, + pd_link) { + if (syspd->pd_id == + detail->args.pd.device_id) { + mtx_lock(&Giant); + device_delete_child( + sc->mfi_dev, + syspd->pd_dev); + mtx_unlock(&Giant); + break; + } + } + } + } + if (detail->code == MR_EVT_PD_INSERTED) { + if (mfi_detect_jbod_change) { + /* Probe for new SYSPD's */ + sx_xlock(&sc->mfi_config_lock); + mtx_lock(&sc->mfi_io_lock); + mfi_syspdprobe(sc); + mtx_unlock(&sc->mfi_io_lock); + sx_xunlock(&sc->mfi_config_lock); + } + } + break; + } +} + +static void +mfi_queue_evt(struct mfi_softc *sc, struct mfi_evt_detail *detail) +{ + struct mfi_evt_queue_elm *elm; + + mtx_assert(&sc->mfi_io_lock, MA_OWNED); + elm = malloc(sizeof(*elm), M_MFIBUF, M_NOWAIT|M_ZERO); + if (elm == NULL) + return; + memcpy(&elm->detail, detail, sizeof(*detail)); + TAILQ_INSERT_TAIL(&sc->mfi_evt_queue, elm, link); + taskqueue_enqueue(taskqueue_swi, &sc->mfi_evt_task); +} + +static void +mfi_handle_evt(void *context, int pending) +{ + TAILQ_HEAD(,mfi_evt_queue_elm) queue; + struct mfi_softc *sc; + struct mfi_evt_queue_elm *elm; + + sc = context; + TAILQ_INIT(&queue); + mtx_lock(&sc->mfi_io_lock); + TAILQ_CONCAT(&queue, &sc->mfi_evt_queue, link); + mtx_unlock(&sc->mfi_io_lock); + while ((elm = TAILQ_FIRST(&queue)) != NULL) { + TAILQ_REMOVE(&queue, elm, link); + mfi_decode_evt(sc, &elm->detail); + free(elm, M_MFIBUF); + } } static int @@ -1112,12 +1615,16 @@ mfi_aen_register(struct mfi_softc *sc, int seq, int locale) < current_aen.members.evt_class) current_aen.members.evt_class = prior_aen.members.evt_class; + mtx_lock(&sc->mfi_io_lock); mfi_abort(sc, sc->mfi_aen_cm); + mtx_unlock(&sc->mfi_io_lock); } } + mtx_lock(&sc->mfi_io_lock); error = mfi_dcmd_command(sc, &cm, MFI_DCMD_CTRL_EVENT_WAIT, (void **)&ed, sizeof(*ed)); + mtx_unlock(&sc->mfi_io_lock); if (error) { goto out; } @@ -1128,10 +1635,13 @@ mfi_aen_register(struct mfi_softc *sc, int seq, int locale) cm->cm_flags = MFI_CMD_DATAIN; cm->cm_complete = mfi_aen_complete; + sc->last_seq_num = seq; sc->mfi_aen_cm = cm; + mtx_lock(&sc->mfi_io_lock); mfi_enqueue_ready(cm); mfi_startio(sc); + mtx_unlock(&sc->mfi_io_lock); out: return (error); @@ -1147,6 +1657,8 @@ mfi_aen_complete(struct mfi_command *cm) int seq = 0, aborted = 0; sc = cm->cm_sc; + mtx_assert(&sc->mfi_io_lock, MA_OWNED); + hdr = &cm->cm_frame->header; if (sc->mfi_aen_cm == NULL) @@ -1163,13 +1675,10 @@ mfi_aen_complete(struct mfi_command *cm) selwakeup(&sc->mfi_select); } detail = cm->cm_data; - /* - * XXX If this function is too expensive or is recursive, then - * events should be put onto a queue and processed later. - */ - mfi_decode_evt(sc, detail); + mfi_queue_evt(sc, detail); seq = detail->seq + 1; - TAILQ_FOREACH_SAFE(mfi_aen_entry, &sc->mfi_aen_pids, aen_link, tmp) { + TAILQ_FOREACH_SAFE(mfi_aen_entry, &sc->mfi_aen_pids, aen_link, + tmp) { TAILQ_REMOVE(&sc->mfi_aen_pids, mfi_aen_entry, aen_link); PROC_LOCK(mfi_aen_entry->p); @@ -1186,7 +1695,9 @@ mfi_aen_complete(struct mfi_command *cm) /* set it up again so the driver can catch more events */ if (!aborted) { + mtx_unlock(&sc->mfi_io_lock); mfi_aen_setup(sc, seq); + mtx_lock(&sc->mfi_io_lock); } } @@ -1212,10 +1723,13 @@ mfi_parse_entries(struct mfi_softc *sc, int start_seq, int stop_seq) return (ENOMEM); for (seq = start_seq;;) { + mtx_lock(&sc->mfi_io_lock); if ((cm = mfi_dequeue_free(sc)) == NULL) { free(el, M_MFIBUF); + mtx_unlock(&sc->mfi_io_lock); return (EBUSY); } + mtx_unlock(&sc->mfi_io_lock); dcmd = &cm->cm_frame->dcmd; bzero(dcmd->mbox, MFI_MBOX_SIZE); @@ -1231,29 +1745,38 @@ mfi_parse_entries(struct mfi_softc *sc, int start_seq, int stop_seq) cm->cm_data = el; cm->cm_len = size; + mtx_lock(&sc->mfi_io_lock); if ((error = mfi_mapcmd(sc, cm)) != 0) { device_printf(sc->mfi_dev, "Failed to get controller entries\n"); mfi_release_command(cm); + mtx_unlock(&sc->mfi_io_lock); break; } + mtx_unlock(&sc->mfi_io_lock); bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap); if (dcmd->header.cmd_status == MFI_STAT_NOT_FOUND) { + mtx_lock(&sc->mfi_io_lock); mfi_release_command(cm); + mtx_unlock(&sc->mfi_io_lock); break; } if (dcmd->header.cmd_status != MFI_STAT_OK) { device_printf(sc->mfi_dev, "Error %d fetching controller entries\n", dcmd->header.cmd_status); + mtx_lock(&sc->mfi_io_lock); mfi_release_command(cm); + mtx_unlock(&sc->mfi_io_lock); break; } + mtx_lock(&sc->mfi_io_lock); mfi_release_command(cm); + mtx_unlock(&sc->mfi_io_lock); for (i = 0; i < el->count; i++) { /* @@ -1269,7 +1792,9 @@ mfi_parse_entries(struct mfi_softc *sc, int start_seq, int stop_seq) else if (el->event[i].seq < start_seq) break; } - mfi_decode_evt(sc, &el->event[i]); + mtx_lock(&sc->mfi_io_lock); + mfi_queue_evt(sc, &el->event[i]); + mtx_unlock(&sc->mfi_io_lock); } seq = el->event[el->count - 1].seq + 1; } @@ -1306,8 +1831,13 @@ mfi_add_ld(struct mfi_softc *sc, int id) free(ld_info, M_MFIBUF); return (0); } - - mfi_add_ld_complete(cm); + if (ld_info->ld_config.params.isSSCD != 1) + mfi_add_ld_complete(cm); + else { + mfi_release_command(cm); + if (ld_info) /* SSCD drives ld_info free here */ + free(ld_info, M_MFIBUF); + } return (0); } @@ -1347,22 +1877,183 @@ mfi_add_ld_complete(struct mfi_command *cm) mtx_lock(&sc->mfi_io_lock); } +static int mfi_add_sys_pd(struct mfi_softc *sc, int id) +{ + struct mfi_command *cm; + struct mfi_dcmd_frame *dcmd = NULL; + struct mfi_pd_info *pd_info = NULL; + int error; + + mtx_assert(&sc->mfi_io_lock, MA_OWNED); + + error = mfi_dcmd_command(sc, &cm, MFI_DCMD_PD_GET_INFO, + (void **)&pd_info, sizeof(*pd_info)); + if (error) { + device_printf(sc->mfi_dev, + "Failed to allocated for MFI_DCMD_PD_GET_INFO %d\n", + error); + if (pd_info) + free(pd_info, M_MFIBUF); + return (error); + } + cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_POLLED; + dcmd = &cm->cm_frame->dcmd; + dcmd->mbox[0]=id; + dcmd->header.scsi_status = 0; + dcmd->header.pad0 = 0; + if (mfi_mapcmd(sc, cm) != 0) { + device_printf(sc->mfi_dev, + "Failed to get physical drive info %d\n", id); + free(pd_info, M_MFIBUF); + return (0); + } + bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap); + mfi_add_sys_pd_complete(cm); + return (0); +} + +static void +mfi_add_sys_pd_complete(struct mfi_command *cm) +{ + struct mfi_frame_header *hdr; + struct mfi_pd_info *pd_info; + struct mfi_softc *sc; + device_t child; + + sc = cm->cm_sc; + hdr = &cm->cm_frame->header; + pd_info = cm->cm_private; + + if (hdr->cmd_status != MFI_STAT_OK) { + free(pd_info, M_MFIBUF); + mfi_release_command(cm); + return; + } + if (pd_info->fw_state != MFI_PD_STATE_SYSTEM) { + device_printf(sc->mfi_dev, "PD=%x is not SYSTEM PD\n", + pd_info->ref.v.device_id); + free(pd_info, M_MFIBUF); + mfi_release_command(cm); + return; + } + mfi_release_command(cm); + + mtx_unlock(&sc->mfi_io_lock); + mtx_lock(&Giant); + if ((child = device_add_child(sc->mfi_dev, "mfisyspd", -1)) == NULL) { + device_printf(sc->mfi_dev, "Failed to add system pd\n"); + free(pd_info, M_MFIBUF); + mtx_unlock(&Giant); + mtx_lock(&sc->mfi_io_lock); + return; + } + + device_set_ivars(child, pd_info); + device_set_desc(child, "MFI System PD"); + bus_generic_attach(sc->mfi_dev); + mtx_unlock(&Giant); + mtx_lock(&sc->mfi_io_lock); +} static struct mfi_command * mfi_bio_command(struct mfi_softc *sc) { - struct mfi_io_frame *io; - struct mfi_command *cm; struct bio *bio; - int flags, blkcount; + struct mfi_command *cm = NULL; - if ((cm = mfi_dequeue_free(sc)) == NULL) + /*reserving two commands to avoid starvation for IOCTL*/ + if (sc->mfi_qstat[MFIQ_FREE].q_length < 2){ return (NULL); - + } if ((bio = mfi_dequeue_bio(sc)) == NULL) { - mfi_release_command(cm); return (NULL); } + if ((uintptr_t)bio->bio_driver2 == MFI_LD_IO) { + cm = mfi_build_ldio(sc, bio); + } else if ((uintptr_t) bio->bio_driver2 == MFI_SYS_PD_IO) { + cm = mfi_build_syspdio(sc, bio); + } + if (!cm) + mfi_enqueue_bio(sc, bio); + return cm; +} +static struct mfi_command * +mfi_build_syspdio(struct mfi_softc *sc, struct bio *bio) +{ + struct mfi_command *cm; + struct mfi_pass_frame *pass; + int flags = 0, blkcount = 0; + uint32_t context = 0; + + if ((cm = mfi_dequeue_free(sc)) == NULL) + return (NULL); + + /* Zero out the MFI frame */ + context = cm->cm_frame->header.context; + bzero(cm->cm_frame, sizeof(union mfi_frame)); + cm->cm_frame->header.context = context; + pass = &cm->cm_frame->pass; + bzero(pass->cdb, 16); + pass->header.cmd = MFI_CMD_PD_SCSI_IO; + switch (bio->bio_cmd & 0x03) { + case BIO_READ: +#define SCSI_READ 0x28 + pass->cdb[0] = SCSI_READ; + flags = MFI_CMD_DATAIN; + break; + case BIO_WRITE: +#define SCSI_WRITE 0x2a + pass->cdb[0] = SCSI_WRITE; + flags = MFI_CMD_DATAOUT; + break; + default: + panic("Invalid bio command"); + } + /* Cheat with the sector length to avoid a non-constant division */ + blkcount = (bio->bio_bcount + MFI_SECTOR_LEN - 1) / MFI_SECTOR_LEN; + /* Fill the LBA and Transfer length in CDB */ + pass->cdb[2] = (bio->bio_pblkno & 0xff000000) >> 24; + pass->cdb[3] = (bio->bio_pblkno & 0x00ff0000) >> 16; + pass->cdb[4] = (bio->bio_pblkno & 0x0000ff00) >> 8; + pass->cdb[5] = bio->bio_pblkno & 0x000000ff; + pass->cdb[7] = (blkcount & 0xff00) >> 8; + pass->cdb[8] = (blkcount & 0x00ff); + pass->header.target_id = (uintptr_t)bio->bio_driver1; + pass->header.timeout = 0; + pass->header.flags = 0; + pass->header.scsi_status = 0; + pass->header.sense_len = MFI_SENSE_LEN; + pass->header.data_len = bio->bio_bcount; + pass->header.cdb_len = 10; + pass->sense_addr_lo = (uint32_t)cm->cm_sense_busaddr; + pass->sense_addr_hi = (uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32); + cm->cm_complete = mfi_bio_complete; + cm->cm_private = bio; + cm->cm_data = bio->bio_data; + cm->cm_len = bio->bio_bcount; + cm->cm_sg = &pass->sgl; + cm->cm_total_frame_size = MFI_PASS_FRAME_SIZE; + cm->cm_flags = flags; + return (cm); +} + +static struct mfi_command * +mfi_build_ldio(struct mfi_softc *sc, struct bio *bio) +{ + struct mfi_io_frame *io; + struct mfi_command *cm; + int flags, blkcount; + uint32_t context = 0; + + if ((cm = mfi_dequeue_free(sc)) == NULL) + return (NULL); + + /* Zero out the MFI frame */ + context = cm->cm_frame->header.context; + bzero(cm->cm_frame, sizeof(union mfi_frame)); + cm->cm_frame->header.context = context; io = &cm->cm_frame->io; switch (bio->bio_cmd & 0x03) { case BIO_READ: @@ -1382,10 +2073,11 @@ mfi_bio_command(struct mfi_softc *sc) io->header.target_id = (uintptr_t)bio->bio_driver1; io->header.timeout = 0; io->header.flags = 0; + io->header.scsi_status = 0; io->header.sense_len = MFI_SENSE_LEN; io->header.data_len = blkcount; - io->sense_addr_lo = cm->cm_sense_busaddr; - io->sense_addr_hi = 0; + io->sense_addr_lo = (uint32_t)cm->cm_sense_busaddr; + io->sense_addr_hi = (uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32); io->lba_hi = (bio->bio_pblkno & 0xffffffff00000000) >> 32; io->lba_lo = bio->bio_pblkno & 0xffffffff; cm->cm_complete = mfi_bio_complete; @@ -1458,14 +2150,14 @@ mfi_startio(struct mfi_softc *sc) } } -static int +int mfi_mapcmd(struct mfi_softc *sc, struct mfi_command *cm) { int error, polled; mtx_assert(&sc->mfi_io_lock, MA_OWNED); - if (cm->cm_data != NULL) { + if ((cm->cm_data != NULL) && (cm->cm_frame->header.cmd != MFI_CMD_STP )) { polled = (cm->cm_flags & MFI_CMD_POLLED) ? BUS_DMA_NOWAIT : 0; error = bus_dmamap_load(sc->mfi_buffer_dmat, cm->cm_dmamap, cm->cm_data, cm->cm_len, mfi_data_cb, cm, polled); @@ -1474,7 +2166,10 @@ mfi_mapcmd(struct mfi_softc *sc, struct mfi_command *cm) return (0); } } else { - error = mfi_send_frame(sc, cm); + if (sc->MFA_enabled) + error = mfi_tbolt_send_frame(sc, cm); + else + error = mfi_send_frame(sc, cm); } return (error); @@ -1488,6 +2183,7 @@ mfi_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) union mfi_sgl *sgl; struct mfi_softc *sc; int i, j, first, dir; + int sge_size; cm = (struct mfi_command *)arg; sc = cm->cm_sc; @@ -1500,34 +2196,54 @@ mfi_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) mfi_complete(sc, cm); return; } - - j = 0; - if (cm->cm_frame->header.cmd == MFI_CMD_STP) { - first = cm->cm_stp_len; - if ((sc->mfi_flags & MFI_FLAGS_SG64) == 0) { - sgl->sg32[j].addr = segs[0].ds_addr; - sgl->sg32[j++].len = first; - } else { - sgl->sg64[j].addr = segs[0].ds_addr; - sgl->sg64[j++].len = first; - } - } else - first = 0; - if ((sc->mfi_flags & MFI_FLAGS_SG64) == 0) { + /* Use IEEE sgl only for IO's on a SKINNY controller + * For other commands on a SKINNY controller use either + * sg32 or sg64 based on the sizeof(bus_addr_t). + * Also calculate the total frame size based on the type + * of SGL used. + */ + if (((cm->cm_frame->header.cmd == MFI_CMD_PD_SCSI_IO) || + (cm->cm_frame->header.cmd == MFI_CMD_LD_READ) || + (cm->cm_frame->header.cmd == MFI_CMD_LD_WRITE)) && + (sc->mfi_flags & MFI_FLAGS_SKINNY)) { for (i = 0; i < nsegs; i++) { - sgl->sg32[j].addr = segs[i].ds_addr + first; - sgl->sg32[j++].len = segs[i].ds_len - first; - first = 0; + sgl->sg_skinny[i].addr = segs[i].ds_addr; + sgl->sg_skinny[i].len = segs[i].ds_len; + sgl->sg_skinny[i].flag = 0; } + hdr->flags |= MFI_FRAME_IEEE_SGL | MFI_FRAME_SGL64; + sge_size = sizeof(struct mfi_sg_skinny); + hdr->sg_count = nsegs; } else { - for (i = 0; i < nsegs; i++) { - sgl->sg64[j].addr = segs[i].ds_addr + first; - sgl->sg64[j++].len = segs[i].ds_len - first; + j = 0; + if (cm->cm_frame->header.cmd == MFI_CMD_STP) { + first = cm->cm_stp_len; + if ((sc->mfi_flags & MFI_FLAGS_SG64) == 0) { + sgl->sg32[j].addr = segs[0].ds_addr; + sgl->sg32[j++].len = first; + } else { + sgl->sg64[j].addr = segs[0].ds_addr; + sgl->sg64[j++].len = first; + } + } else first = 0; + if ((sc->mfi_flags & MFI_FLAGS_SG64) == 0) { + for (i = 0; i < nsegs; i++) { + sgl->sg32[j].addr = segs[i].ds_addr + first; + sgl->sg32[j++].len = segs[i].ds_len - first; + first = 0; + } + } else { + for (i = 0; i < nsegs; i++) { + sgl->sg64[j].addr = segs[i].ds_addr + first; + sgl->sg64[j++].len = segs[i].ds_len - first; + first = 0; + } + hdr->flags |= MFI_FRAME_SGL64; } - hdr->flags |= MFI_FRAME_SGL64; + hdr->sg_count = j; + sge_size = sc->mfi_sge_size; } - hdr->sg_count = j; dir = 0; if (cm->cm_flags & MFI_CMD_DATAIN) { @@ -1538,8 +2254,6 @@ mfi_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) dir |= BUS_DMASYNC_PREWRITE; hdr->flags |= MFI_FRAME_DIR_WRITE; } - if (cm->cm_frame->header.cmd == MFI_CMD_STP) - dir |= BUS_DMASYNC_PREWRITE; bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap, dir); cm->cm_flags |= MFI_CMD_MAPPED; @@ -1552,7 +2266,10 @@ mfi_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) cm->cm_total_frame_size += (sc->mfi_sge_size * nsegs); cm->cm_extra_frames = (cm->cm_total_frame_size - 1) / MFI_FRAME_SIZE; - mfi_send_frame(sc, cm); + if (sc->MFA_enabled) + mfi_tbolt_send_frame(sc, cm); + else + mfi_send_frame(sc, cm); return; } @@ -1588,7 +2305,7 @@ mfi_send_frame(struct mfi_softc *sc, struct mfi_command *cm) if (cm->cm_extra_frames > 7) cm->cm_extra_frames = 7; - sc->mfi_issue_cmd(sc,cm->cm_frame_busaddr,cm->cm_extra_frames); + sc->mfi_issue_cmd(sc, cm->cm_frame_busaddr, cm->cm_extra_frames); if ((cm->cm_flags & MFI_CMD_POLLED) == 0) return (0); @@ -1603,14 +2320,15 @@ mfi_send_frame(struct mfi_softc *sc, struct mfi_command *cm) if (hdr->cmd_status == MFI_STAT_INVALID_STATUS) { device_printf(sc->mfi_dev, "Frame %p timed out " - "command 0x%X\n", hdr, cm->cm_frame->dcmd.opcode); + "command 0x%X\n", hdr, cm->cm_frame->dcmd.opcode); return (ETIMEDOUT); } return (0); } -static void + +void mfi_complete(struct mfi_softc *sc, struct mfi_command *cm) { int dir; @@ -1642,6 +2360,7 @@ mfi_abort(struct mfi_softc *sc, struct mfi_command *cm_abort) struct mfi_command *cm; struct mfi_abort_frame *abort; int i = 0; + uint32_t context = 0; mtx_assert(&sc->mfi_io_lock, MA_OWNED); @@ -1649,21 +2368,30 @@ mfi_abort(struct mfi_softc *sc, struct mfi_command *cm_abort) return (EBUSY); } + /* Zero out the MFI frame */ + context = cm->cm_frame->header.context; + bzero(cm->cm_frame, sizeof(union mfi_frame)); + cm->cm_frame->header.context = context; + abort = &cm->cm_frame->abort; abort->header.cmd = MFI_CMD_ABORT; abort->header.flags = 0; + abort->header.scsi_status = 0; abort->abort_context = cm_abort->cm_frame->header.context; - abort->abort_mfi_addr_lo = cm_abort->cm_frame_busaddr; - abort->abort_mfi_addr_hi = 0; + abort->abort_mfi_addr_lo = (uint32_t)cm_abort->cm_frame_busaddr; + abort->abort_mfi_addr_hi = + (uint32_t)((uint64_t)cm_abort->cm_frame_busaddr >> 32); cm->cm_data = NULL; cm->cm_flags = MFI_CMD_POLLED; - sc->mfi_aen_cm->cm_aen_abort = 1; + if (sc->mfi_aen_cm) + sc->mfi_aen_cm->cm_aen_abort = 1; mfi_mapcmd(sc, cm); mfi_release_command(cm); while (i < 5 && sc->mfi_aen_cm != NULL) { - msleep(&sc->mfi_aen_cm, &sc->mfi_io_lock, 0, "mfiabort", 5 * hz); + msleep(&sc->mfi_aen_cm, &sc->mfi_io_lock, 0, "mfiabort", + 5 * hz); i++; } @@ -1671,24 +2399,32 @@ mfi_abort(struct mfi_softc *sc, struct mfi_command *cm_abort) } int -mfi_dump_blocks(struct mfi_softc *sc, int id, uint64_t lba, void *virt, int len) +mfi_dump_blocks(struct mfi_softc *sc, int id, uint64_t lba, void *virt, + int len) { struct mfi_command *cm; struct mfi_io_frame *io; int error; + uint32_t context = 0; if ((cm = mfi_dequeue_free(sc)) == NULL) return (EBUSY); + /* Zero out the MFI frame */ + context = cm->cm_frame->header.context; + bzero(cm->cm_frame, sizeof(union mfi_frame)); + cm->cm_frame->header.context = context; + io = &cm->cm_frame->io; io->header.cmd = MFI_CMD_LD_WRITE; io->header.target_id = id; io->header.timeout = 0; io->header.flags = 0; + io->header.scsi_status = 0; io->header.sense_len = MFI_SENSE_LEN; io->header.data_len = (len + MFI_SECTOR_LEN - 1) / MFI_SECTOR_LEN; - io->sense_addr_lo = cm->cm_sense_busaddr; - io->sense_addr_hi = 0; + io->sense_addr_lo = (uint32_t)cm->cm_sense_busaddr; + io->sense_addr_hi = (uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32); io->lba_hi = (lba & 0xffffffff00000000) >> 32; io->lba_lo = lba & 0xffffffff; cm->cm_data = virt; @@ -1706,6 +2442,53 @@ mfi_dump_blocks(struct mfi_softc *sc, int id, uint64_t lba, void *virt, int len) return (error); } +int +mfi_dump_syspd_blocks(struct mfi_softc *sc, int id, uint64_t lba, void *virt, + int len) +{ + struct mfi_command *cm; + struct mfi_pass_frame *pass; + int error; + int blkcount = 0; + + if ((cm = mfi_dequeue_free(sc)) == NULL) + return (EBUSY); + + pass = &cm->cm_frame->pass; + bzero(pass->cdb, 16); + pass->header.cmd = MFI_CMD_PD_SCSI_IO; + pass->cdb[0] = SCSI_WRITE; + pass->cdb[2] = (lba & 0xff000000) >> 24; + pass->cdb[3] = (lba & 0x00ff0000) >> 16; + pass->cdb[4] = (lba & 0x0000ff00) >> 8; + pass->cdb[5] = (lba & 0x000000ff); + blkcount = (len + MFI_SECTOR_LEN - 1) / MFI_SECTOR_LEN; + pass->cdb[7] = (blkcount & 0xff00) >> 8; + pass->cdb[8] = (blkcount & 0x00ff); + pass->header.target_id = id; + pass->header.timeout = 0; + pass->header.flags = 0; + pass->header.scsi_status = 0; + pass->header.sense_len = MFI_SENSE_LEN; + pass->header.data_len = len; + pass->header.cdb_len = 10; + pass->sense_addr_lo = (uint32_t)cm->cm_sense_busaddr; + pass->sense_addr_hi = (uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32); + cm->cm_data = virt; + cm->cm_len = len; + cm->cm_sg = &pass->sgl; + cm->cm_total_frame_size = MFI_PASS_FRAME_SIZE; + cm->cm_flags = MFI_CMD_POLLED | MFI_CMD_DATAOUT; + + error = mfi_mapcmd(sc, cm); + bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap); + mfi_release_command(cm); + + return (error); +} + static int mfi_open(struct cdev *dev, int flags, int fmt, struct thread *td) { @@ -1771,12 +2554,18 @@ mfi_config_unlock(struct mfi_softc *sc, int locked) sx_xunlock(&sc->mfi_config_lock); } -/* Perform pre-issue checks on commands from userland and possibly veto them. */ +/* + * Perform pre-issue checks on commands from userland and possibly veto + * them. + */ static int mfi_check_command_pre(struct mfi_softc *sc, struct mfi_command *cm) { struct mfi_disk *ld, *ld2; int error; + struct mfi_system_pd *syspd = NULL; + uint16_t syspd_id; + uint16_t *mbox; mtx_assert(&sc->mfi_io_lock, MA_OWNED); error = 0; @@ -1805,6 +2594,20 @@ mfi_check_command_pre(struct mfi_softc *sc, struct mfi_command *cm) } } break; + case MFI_DCMD_PD_STATE_SET: + mbox = (uint16_t *) cm->cm_frame->dcmd.mbox; + syspd_id = mbox[0]; + if (mbox[2] == MFI_PD_STATE_UNCONFIGURED_GOOD) { + TAILQ_FOREACH(syspd, &sc->mfi_syspd_tqh, pd_link) { + if (syspd->pd_id == syspd_id) + break; + } + } + else + break; + if (syspd) + error = mfi_syspd_disable(syspd); + break; default: break; } @@ -1816,6 +2619,9 @@ static void mfi_check_command_post(struct mfi_softc *sc, struct mfi_command *cm) { struct mfi_disk *ld, *ldn; + struct mfi_system_pd *syspd = NULL; + uint16_t syspd_id; + uint16_t *mbox; switch (cm->cm_frame->dcmd.opcode) { case MFI_DCMD_LD_DELETE: @@ -1853,9 +2659,158 @@ mfi_check_command_post(struct mfi_softc *sc, struct mfi_command *cm) case MFI_DCMD_CFG_FOREIGN_IMPORT: mfi_ldprobe(sc); break; + case MFI_DCMD_PD_STATE_SET: + mbox = (uint16_t *) cm->cm_frame->dcmd.mbox; + syspd_id = mbox[0]; + if (mbox[2] == MFI_PD_STATE_UNCONFIGURED_GOOD) { + TAILQ_FOREACH(syspd, &sc->mfi_syspd_tqh,pd_link) { + if (syspd->pd_id == syspd_id) + break; + } + } + else + break; + /* If the transition fails then enable the syspd again */ + if (syspd && cm->cm_frame->header.cmd_status != MFI_STAT_OK) + mfi_syspd_enable(syspd); + break; } } +static int mfi_check_for_sscd(struct mfi_softc *sc, struct mfi_command *cm) +{ + struct mfi_config_data *conf_data=(struct mfi_config_data *)cm->cm_data; + struct mfi_command *ld_cm = NULL; + struct mfi_ld_info *ld_info = NULL; + int error = 0; + + if ((cm->cm_frame->dcmd.opcode == MFI_DCMD_CFG_ADD) && + (conf_data->ld[0].params.isSSCD == 1)){ + error = 1; + } else if (cm->cm_frame->dcmd.opcode == MFI_DCMD_LD_DELETE) { + error = mfi_dcmd_command (sc, &ld_cm, MFI_DCMD_LD_GET_INFO, + (void **)&ld_info, sizeof(*ld_info)); + if (error){ + device_printf(sc->mfi_dev, "Failed to allocate" + "MFI_DCMD_LD_GET_INFO %d", error); + if (ld_info) + free(ld_info, M_MFIBUF); + return 0; + } + ld_cm->cm_flags = MFI_CMD_DATAIN; + ld_cm->cm_frame->dcmd.mbox[0]= cm->cm_frame->dcmd.mbox[0]; + ld_cm->cm_frame->header.target_id = cm->cm_frame->dcmd.mbox[0]; + if (mfi_wait_command(sc, ld_cm) != 0){ + device_printf(sc->mfi_dev, "failed to get log drv\n"); + mfi_release_command(ld_cm); + free(ld_info, M_MFIBUF); + return 0; + } + + if (ld_cm->cm_frame->header.cmd_status != MFI_STAT_OK) { + free(ld_info, M_MFIBUF); + mfi_release_command(ld_cm); + return 0; + } + else + ld_info = (struct mfi_ld_info *)ld_cm->cm_private; + + if (ld_info->ld_config.params.isSSCD == 1) + error = 1; + + mfi_release_command(ld_cm); + free(ld_info, M_MFIBUF); + + } + return error; +} + +static int +mfi_stp_cmd(struct mfi_softc *sc, struct mfi_command *cm,caddr_t arg) +{ + uint8_t i; + struct mfi_ioc_packet *ioc; + ioc = (struct mfi_ioc_packet *)arg; + int sge_size, error; + struct megasas_sge *kern_sge; + + memset(sc->kbuff_arr, 0, sizeof(sc->kbuff_arr)); + kern_sge =(struct megasas_sge *) ((uintptr_t)cm->cm_frame + ioc->mfi_sgl_off); + cm->cm_frame->header.sg_count = ioc->mfi_sge_count; + + if (sizeof(bus_addr_t) == 8) { + cm->cm_frame->header.flags |= MFI_FRAME_SGL64; + cm->cm_extra_frames = 2; + sge_size = sizeof(struct mfi_sg64); + } else { + cm->cm_extra_frames = (cm->cm_total_frame_size - 1) / MFI_FRAME_SIZE; + sge_size = sizeof(struct mfi_sg32); + } + + cm->cm_total_frame_size += (sge_size * ioc->mfi_sge_count); + for (i = 0; i < ioc->mfi_sge_count; i++) { + if (bus_dma_tag_create( sc->mfi_parent_dmat, /* parent */ + 1, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + ioc->mfi_sgl[i].iov_len,/* maxsize */ + 2, /* nsegments */ + ioc->mfi_sgl[i].iov_len,/* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->mfi_kbuff_arr_dmat[i])) { + device_printf(sc->mfi_dev, + "Cannot allocate mfi_kbuff_arr_dmat tag\n"); + return (ENOMEM); + } + + if (bus_dmamem_alloc(sc->mfi_kbuff_arr_dmat[i], + (void **)&sc->kbuff_arr[i], BUS_DMA_NOWAIT, + &sc->mfi_kbuff_arr_dmamap[i])) { + device_printf(sc->mfi_dev, + "Cannot allocate mfi_kbuff_arr_dmamap memory\n"); + return (ENOMEM); + } + + bus_dmamap_load(sc->mfi_kbuff_arr_dmat[i], + sc->mfi_kbuff_arr_dmamap[i], sc->kbuff_arr[i], + ioc->mfi_sgl[i].iov_len, mfi_addr_cb, + &sc->mfi_kbuff_arr_busaddr[i], 0); + + if (!sc->kbuff_arr[i]) { + device_printf(sc->mfi_dev, + "Could not allocate memory for kbuff_arr info\n"); + return -1; + } + kern_sge[i].phys_addr = sc->mfi_kbuff_arr_busaddr[i]; + kern_sge[i].length = ioc->mfi_sgl[i].iov_len; + + if (sizeof(bus_addr_t) == 8) { + cm->cm_frame->stp.sgl.sg64[i].addr = + kern_sge[i].phys_addr; + cm->cm_frame->stp.sgl.sg64[i].len = + ioc->mfi_sgl[i].iov_len; + } else { + cm->cm_frame->stp.sgl.sg32[i].len = + kern_sge[i].phys_addr; + cm->cm_frame->stp.sgl.sg32[i].len = + ioc->mfi_sgl[i].iov_len; + } + + error = copyin(ioc->mfi_sgl[i].iov_base, + sc->kbuff_arr[i], + ioc->mfi_sgl[i].iov_len); + if (error != 0) { + device_printf(sc->mfi_dev, "Copy in failed\n"); + return error; + } + } + + cm->cm_flags |=MFI_CMD_MAPPED; + return 0; +} + static int mfi_user_command(struct mfi_softc *sc, struct mfi_ioc_passthru *ioc) { @@ -1924,11 +2879,7 @@ out: return (error); } -#ifdef __amd64__ #define PTRIN(p) ((void *)(uintptr_t)(p)) -#else -#define PTRIN(p) (p) -#endif static int mfi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) @@ -1936,26 +2887,35 @@ mfi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td struct mfi_softc *sc; union mfi_statrequest *ms; struct mfi_ioc_packet *ioc; -#ifdef __amd64__ +#ifdef COMPAT_FREEBSD32 struct mfi_ioc_packet32 *ioc32; #endif struct mfi_ioc_aen *aen; struct mfi_command *cm = NULL; - uint32_t context; + uint32_t context = 0; union mfi_sense_ptr sense_ptr; - uint8_t *data = NULL, *temp, *addr; + uint8_t *data = NULL, *temp, *addr, skip_pre_post = 0; size_t len; - int i; + int i, res; struct mfi_ioc_passthru *iop = (struct mfi_ioc_passthru *)arg; -#ifdef __amd64__ +#ifdef COMPAT_FREEBSD32 struct mfi_ioc_passthru32 *iop32 = (struct mfi_ioc_passthru32 *)arg; struct mfi_ioc_passthru iop_swab; #endif int error, locked; - + union mfi_sgl *sgl; sc = dev->si_drv1; error = 0; + if (sc->adpreset) + return EBUSY; + + if (sc->hw_crit_error) + return EBUSY; + + if (sc->issuepend_done == 0) + return EBUSY; + switch (cmd) { case MFIIO_STATS: ms = (union mfi_statrequest *)arg; @@ -1997,7 +2957,7 @@ mfi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td break; } case MFI_CMD: -#ifdef __amd64__ +#ifdef COMPAT_FREEBSD32 case MFI_CMD32: #endif { @@ -2023,15 +2983,19 @@ mfi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td * will clobber some data */ context = cm->cm_frame->header.context; + cm->cm_frame->header.context = cm->cm_index; bcopy(ioc->mfi_frame.raw, cm->cm_frame, - 2 * MFI_DCMD_FRAME_SIZE); /* this isn't quite right */ + 2 * MEGAMFI_FRAME_SIZE); cm->cm_total_frame_size = (sizeof(union mfi_sgl) * ioc->mfi_sge_count) + ioc->mfi_sgl_off; + cm->cm_frame->header.scsi_status = 0; + cm->cm_frame->header.pad0 = 0; if (ioc->mfi_sge_count) { cm->cm_sg = (union mfi_sgl *)&cm->cm_frame->bytes[ioc->mfi_sgl_off]; } + sgl = cm->cm_sg; cm->cm_flags = 0; if (cm->cm_frame->header.flags & MFI_FRAME_DATAIN) cm->cm_flags |= MFI_CMD_DATAIN; @@ -2042,12 +3006,12 @@ mfi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td cm->cm_flags |= MFI_CMD_DATAIN | MFI_CMD_DATAOUT; cm->cm_len = cm->cm_frame->header.data_len; if (cm->cm_frame->header.cmd == MFI_CMD_STP) { -#ifdef __amd64__ +#ifdef COMPAT_FREEBSD32 if (cmd == MFI_CMD) { #endif /* Native */ cm->cm_stp_len = ioc->mfi_sgl[0].iov_len; -#ifdef __amd64__ +#ifdef COMPAT_FREEBSD32 } else { /* 32bit on 64bit */ ioc32 = (struct mfi_ioc_packet32 *)ioc; @@ -2071,84 +3035,97 @@ mfi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td /* restore header context */ cm->cm_frame->header.context = context; - temp = data; - if ((cm->cm_flags & MFI_CMD_DATAOUT) || - (cm->cm_frame->header.cmd == MFI_CMD_STP)) { - for (i = 0; i < ioc->mfi_sge_count; i++) { -#ifdef __amd64__ - if (cmd == MFI_CMD) { + if (cm->cm_frame->header.cmd == MFI_CMD_STP) { + res = mfi_stp_cmd(sc, cm, arg); + if (res != 0) + goto out; + } else { + temp = data; + if ((cm->cm_flags & MFI_CMD_DATAOUT) || + (cm->cm_frame->header.cmd == MFI_CMD_STP)) { + for (i = 0; i < ioc->mfi_sge_count; i++) { +#ifdef COMPAT_FREEBSD32 + if (cmd == MFI_CMD) { #endif - /* Native */ - addr = ioc->mfi_sgl[i].iov_base; - len = ioc->mfi_sgl[i].iov_len; -#ifdef __amd64__ - } else { - /* 32bit on 64bit */ - ioc32 = (struct mfi_ioc_packet32 *)ioc; - addr = PTRIN(ioc32->mfi_sgl[i].iov_base); - len = ioc32->mfi_sgl[i].iov_len; - } + /* Native */ + addr = ioc->mfi_sgl[i].iov_base; + len = ioc->mfi_sgl[i].iov_len; +#ifdef COMPAT_FREEBSD32 + } else { + /* 32bit on 64bit */ + ioc32 = (struct mfi_ioc_packet32 *)ioc; + addr = PTRIN(ioc32->mfi_sgl[i].iov_base); + len = ioc32->mfi_sgl[i].iov_len; + } #endif - error = copyin(addr, temp, len); - if (error != 0) { - device_printf(sc->mfi_dev, - "Copy in failed\n"); - goto out; + error = copyin(addr, temp, len); + if (error != 0) { + device_printf(sc->mfi_dev, + "Copy in failed\n"); + goto out; + } + temp = &temp[len]; } - temp = &temp[len]; } } if (cm->cm_frame->header.cmd == MFI_CMD_DCMD) - locked = mfi_config_lock(sc, cm->cm_frame->dcmd.opcode); + locked = mfi_config_lock(sc, + cm->cm_frame->dcmd.opcode); if (cm->cm_frame->header.cmd == MFI_CMD_PD_SCSI_IO) { - cm->cm_frame->pass.sense_addr_lo = cm->cm_sense_busaddr; - cm->cm_frame->pass.sense_addr_hi = 0; + cm->cm_frame->pass.sense_addr_lo = + (uint32_t)cm->cm_sense_busaddr; + cm->cm_frame->pass.sense_addr_hi = + (uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32); } - mtx_lock(&sc->mfi_io_lock); - error = mfi_check_command_pre(sc, cm); - if (error) { - mtx_unlock(&sc->mfi_io_lock); - goto out; + skip_pre_post = mfi_check_for_sscd (sc, cm); + if (!skip_pre_post) { + error = mfi_check_command_pre(sc, cm); + if (error) { + mtx_unlock(&sc->mfi_io_lock); + goto out; + } } - if ((error = mfi_wait_command(sc, cm)) != 0) { device_printf(sc->mfi_dev, "Controller polled failed\n"); mtx_unlock(&sc->mfi_io_lock); goto out; } - - mfi_check_command_post(sc, cm); + if (!skip_pre_post) { + mfi_check_command_post(sc, cm); + } mtx_unlock(&sc->mfi_io_lock); - temp = data; - if ((cm->cm_flags & MFI_CMD_DATAIN) || - (cm->cm_frame->header.cmd == MFI_CMD_STP)) { - for (i = 0; i < ioc->mfi_sge_count; i++) { -#ifdef __amd64__ - if (cmd == MFI_CMD) { + if (cm->cm_frame->header.cmd != MFI_CMD_STP) { + temp = data; + if ((cm->cm_flags & MFI_CMD_DATAIN) || + (cm->cm_frame->header.cmd == MFI_CMD_STP)) { + for (i = 0; i < ioc->mfi_sge_count; i++) { +#ifdef COMPAT_FREEBSD32 + if (cmd == MFI_CMD) { #endif - /* Native */ - addr = ioc->mfi_sgl[i].iov_base; - len = ioc->mfi_sgl[i].iov_len; -#ifdef __amd64__ - } else { - /* 32bit on 64bit */ - ioc32 = (struct mfi_ioc_packet32 *)ioc; - addr = PTRIN(ioc32->mfi_sgl[i].iov_base); - len = ioc32->mfi_sgl[i].iov_len; - } + /* Native */ + addr = ioc->mfi_sgl[i].iov_base; + len = ioc->mfi_sgl[i].iov_len; +#ifdef COMPAT_FREEBSD32 + } else { + /* 32bit on 64bit */ + ioc32 = (struct mfi_ioc_packet32 *)ioc; + addr = PTRIN(ioc32->mfi_sgl[i].iov_base); + len = ioc32->mfi_sgl[i].iov_len; + } #endif - error = copyout(temp, addr, len); - if (error != 0) { - device_printf(sc->mfi_dev, - "Copy out failed\n"); - goto out; + error = copyout(temp, addr, len); + if (error != 0) { + device_printf(sc->mfi_dev, + "Copy out failed\n"); + goto out; + } + temp = &temp[len]; } - temp = &temp[len]; } } @@ -2157,7 +3134,7 @@ mfi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td bcopy(&ioc->mfi_frame.raw[ioc->mfi_sense_off], &sense_ptr.sense_ptr_data[0], sizeof(sense_ptr.sense_ptr_data)); -#ifdef __amd64__ +#ifdef COMPAT_FREEBSD32 if (cmd != MFI_CMD) { /* * not 64bit native so zero out any address @@ -2179,6 +3156,26 @@ out: mfi_config_unlock(sc, locked); if (data) free(data, M_MFIBUF); + if (cm->cm_frame->header.cmd == MFI_CMD_STP) { + for (i = 0; i < 2; i++) { + if (sc->kbuff_arr[i]) { + if (sc->mfi_kbuff_arr_busaddr != 0) + bus_dmamap_unload( + sc->mfi_kbuff_arr_dmat[i], + sc->mfi_kbuff_arr_dmamap[i] + ); + if (sc->kbuff_arr[i] != NULL) + bus_dmamem_free( + sc->mfi_kbuff_arr_dmat[i], + sc->kbuff_arr[i], + sc->mfi_kbuff_arr_dmamap[i] + ); + if (sc->mfi_kbuff_arr_dmat[i] != NULL) + bus_dma_tag_destroy( + sc->mfi_kbuff_arr_dmat[i]); + } + } + } if (cm) { mtx_lock(&sc->mfi_io_lock); mfi_release_command(cm); @@ -2235,7 +3232,7 @@ out: cmd, arg, flag, td)); break; } -#ifdef __amd64__ +#ifdef COMPAT_FREEBSD32 case MFIIO_PASSTHRU32: iop_swab.ioc_frame = iop32->ioc_frame; iop_swab.buf_size = iop32->buf_size; @@ -2245,7 +3242,7 @@ out: #endif case MFIIO_PASSTHRU: error = mfi_user_command(sc, iop); -#ifdef __amd64__ +#ifdef COMPAT_FREEBSD32 if (cmd == MFIIO_PASSTHRU32) iop32->ioc_frame = iop_swab.ioc_frame; #endif @@ -2268,7 +3265,7 @@ mfi_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct struct mfi_command *cm = NULL; struct mfi_aen *mfi_aen_entry; union mfi_sense_ptr sense_ptr; - uint32_t context; + uint32_t context = 0; uint8_t *data = NULL, *temp; int i; int error, locked; @@ -2303,6 +3300,8 @@ mfi_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct 2 * MFI_DCMD_FRAME_SIZE); /* this isn't quite right */ cm->cm_total_frame_size = (sizeof(union mfi_sgl) * l_ioc.lioc_sge_count) + l_ioc.lioc_sgl_off; + cm->cm_frame->header.scsi_status = 0; + cm->cm_frame->header.pad0 = 0; if (l_ioc.lioc_sge_count) cm->cm_sg = (union mfi_sgl *)&cm->cm_frame->bytes[l_ioc.lioc_sgl_off]; @@ -2346,8 +3345,10 @@ mfi_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct locked = mfi_config_lock(sc, cm->cm_frame->dcmd.opcode); if (cm->cm_frame->header.cmd == MFI_CMD_PD_SCSI_IO) { - cm->cm_frame->pass.sense_addr_lo = cm->cm_sense_busaddr; - cm->cm_frame->pass.sense_addr_hi = 0; + cm->cm_frame->pass.sense_addr_lo = + (uint32_t)cm->cm_sense_busaddr; + cm->cm_frame->pass.sense_addr_hi = + (uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32); } mtx_lock(&sc->mfi_io_lock); @@ -2486,7 +3487,6 @@ mfi_poll(struct cdev *dev, int poll_events, struct thread *td) return revents; } - static void mfi_dump_all(void) { @@ -2514,8 +3514,8 @@ mfi_dump_all(void) TAILQ_FOREACH(cm, &sc->mfi_busy, cm_link) { if (cm->cm_timestamp < deadline) { device_printf(sc->mfi_dev, - "COMMAND %p TIMEOUT AFTER %d SECONDS\n", cm, - (int)(time_uptime - cm->cm_timestamp)); + "COMMAND %p TIMEOUT AFTER %d SECONDS\n", + cm, (int)(time_uptime - cm->cm_timestamp)); MFI_PRINT_CMD(cm); timedout++; } @@ -2541,17 +3541,28 @@ mfi_timeout(void *data) int timedout = 0; deadline = time_uptime - MFI_CMD_TIMEOUT; + if (sc->adpreset == 0) { + if (!mfi_tbolt_reset(sc)) { + callout_reset(&sc->mfi_watchdog_callout, MFI_CMD_TIMEOUT * hz, mfi_timeout, sc); + return; + } + } mtx_lock(&sc->mfi_io_lock); TAILQ_FOREACH(cm, &sc->mfi_busy, cm_link) { if (sc->mfi_aen_cm == cm) continue; if ((sc->mfi_aen_cm != cm) && (cm->cm_timestamp < deadline)) { - device_printf(sc->mfi_dev, - "COMMAND %p TIMEOUT AFTER %d SECONDS\n", cm, - (int)(time_uptime - cm->cm_timestamp)); - MFI_PRINT_CMD(cm); - MFI_VALIDATE_CMD(sc, cm); - timedout++; + if (sc->adpreset != 0 && sc->issuepend_done == 0) { + cm->cm_timestamp = time_uptime; + } else { + device_printf(sc->mfi_dev, + "COMMAND %p TIMEOUT AFTER %d SECONDS\n", + cm, (int)(time_uptime - cm->cm_timestamp) + ); + MFI_PRINT_CMD(cm); + MFI_VALIDATE_CMD(sc, cm); + timedout++; + } } } diff --git a/sys/dev/mfi/mfi_cam.c b/sys/dev/mfi/mfi_cam.c index aa76a10..f9bef0c 100644 --- a/sys/dev/mfi/mfi_cam.c +++ b/sys/dev/mfi/mfi_cam.c @@ -270,12 +270,18 @@ mfip_start(void *data) struct mfip_softc *sc; struct mfi_pass_frame *pt; struct mfi_command *cm; + uint32_t context = 0; sc = ccbh->ccb_mfip_ptr; if ((cm = mfi_dequeue_free(sc->mfi_sc)) == NULL) return (NULL); + /* Zero out the MFI frame */ + context = cm->cm_frame->header.context; + bzero(cm->cm_frame, sizeof(union mfi_frame)); + cm->cm_frame->header.context = context; + pt = &cm->cm_frame->pass; pt->header.cmd = MFI_CMD_PD_SCSI_IO; pt->header.cmd_status = 0; diff --git a/sys/dev/mfi/mfi_debug.c b/sys/dev/mfi/mfi_debug.c index 8fe7778..d6f4c9a 100644 --- a/sys/dev/mfi/mfi_debug.c +++ b/sys/dev/mfi/mfi_debug.c @@ -63,7 +63,8 @@ mfi_print_frame_flags(device_t dev, uint32_t flags) "\2SGL64" "\3SENSE64" "\4WRITE" - "\5READ"); + "\5READ" + "\6IEEESGL"); } static void @@ -73,7 +74,15 @@ mfi_print_sgl(struct mfi_frame_header *hdr, union mfi_sgl *sgl, int count) printf("SG List:\n"); for (i = 0; i < count; i++) { - if (hdr->flags & MFI_FRAME_SGL64) { + if (hdr->flags & MFI_FRAME_IEEE_SGL) { + printf("0x%lx:%06d ", (u_long)sgl->sg_skinny[i].addr, + sgl->sg_skinny[i].len); + columns += 26; + if (columns > 77) { + printf("\n"); + columns = 0; + } + } else if (hdr->flags & MFI_FRAME_SGL64) { printf("0x%lx:%06d ", (u_long)sgl->sg64[i].addr, sgl->sg64[i].len); columns += 26; @@ -240,7 +249,12 @@ mfi_validate_sg(struct mfi_softc *sc, struct mfi_command *cm, hdr = &cm->cm_frame->header; count = 0; for (i = 0; i < hdr->sg_count; i++) { - count += cm->cm_sg->sg32[i].len; + if (hdr->flags & MFI_FRAME_IEEE_SGL) + count += cm->cm_sg->sg_skinny[i].len; + else if (hdr->flags & MFI_FRAME_SGL64) + count += cm->cm_sg->sg64[i].len; + else + count += cm->cm_sg->sg32[i].len; } /* count++; diff --git a/sys/dev/mfi/mfi_disk.c b/sys/dev/mfi/mfi_disk.c index ad656d3c..15c9d76 100644 --- a/sys/dev/mfi/mfi_disk.c +++ b/sys/dev/mfi/mfi_disk.c @@ -224,7 +224,7 @@ mfi_disk_disable(struct mfi_disk *sc) if (sc->ld_flags & MFI_DISK_FLAGS_OPEN) { if (sc->ld_controller->mfi_delete_busy_volumes) return (0); - device_printf(sc->ld_dev, "Unable to delete busy device\n"); + device_printf(sc->ld_dev, "Unable to delete busy ld device\n"); return (EBUSY); } sc->ld_flags |= MFI_DISK_FLAGS_DISABLED; @@ -246,6 +246,7 @@ mfi_disk_strategy(struct bio *bio) struct mfi_softc *controller; sc = bio->bio_disk->d_drv1; + controller = sc->ld_controller; if (sc == NULL) { bio->bio_error = EINVAL; @@ -255,8 +256,24 @@ mfi_disk_strategy(struct bio *bio) return; } - controller = sc->ld_controller; + if (controller->adpreset){ + bio->bio_error = EBUSY; + return; + } + + if (controller->hw_crit_error){ + bio->bio_error = EBUSY; + return; + } + + if (controller->issuepend_done == 0){ + bio->bio_error = EBUSY; + return; + } + bio->bio_driver1 = (void *)(uintptr_t)sc->ld_id; + /* Mark it as LD IO */ + bio->bio_driver2 = (void *)MFI_LD_IO; mtx_lock(&controller->mfi_io_lock); mfi_enqueue_bio(controller, bio); mfi_startio(controller); diff --git a/sys/dev/mfi/mfi_ioctl.h b/sys/dev/mfi/mfi_ioctl.h index 48e9c7f..e420017 100644 --- a/sys/dev/mfi/mfi_ioctl.h +++ b/sys/dev/mfi/mfi_ioctl.h @@ -28,13 +28,18 @@ __FBSDID("$FreeBSD$"); #include <dev/mfi/mfireg.h> +#include <machine/bus.h> -#if defined(__amd64__) /* Assume amd64 wants 32 bit Linux */ struct iovec32 { u_int32_t iov_base; int iov_len; }; -#endif + +struct megasas_sge +{ + bus_addr_t phys_addr; + uint32_t length; +}; #define MFIQ_FREE 0 #define MFIQ_BIO 1 @@ -79,7 +84,7 @@ struct mfi_ioc_packet { struct iovec mfi_sgl[MAX_IOCTL_SGE]; } __packed; -#ifdef __amd64__ +#ifdef COMPAT_FREEBSD32 struct mfi_ioc_packet32 { uint16_t mfi_adapter_no; uint16_t mfi_pad1; @@ -104,7 +109,7 @@ struct mfi_ioc_aen { } __packed; #define MFI_CMD _IOWR('M', 1, struct mfi_ioc_packet) -#ifdef __amd64__ +#ifdef COMPAT_FREEBSD32 #define MFI_CMD32 _IOWR('M', 1, struct mfi_ioc_packet32) #endif #define MFI_SET_AEN _IOW('M', 3, struct mfi_ioc_aen) @@ -136,7 +141,7 @@ struct mfi_ioc_passthru { uint8_t *buf; } __packed; -#ifdef __amd64__ +#ifdef COMPAT_FREEBSD32 struct mfi_ioc_passthru32 { struct mfi_dcmd_frame ioc_frame; uint32_t buf_size; @@ -146,7 +151,7 @@ struct mfi_ioc_passthru32 { #define MFIIO_STATS _IOWR('Q', 101, union mfi_statrequest) #define MFIIO_PASSTHRU _IOWR('C', 102, struct mfi_ioc_passthru) -#ifdef __amd64__ +#ifdef COMPAT_FREEBSD32 #define MFIIO_PASSTHRU32 _IOWR('C', 102, struct mfi_ioc_passthru32) #endif diff --git a/sys/dev/mfi/mfi_linux.c b/sys/dev/mfi/mfi_linux.c index 12135ff..3328a66 100644 --- a/sys/dev/mfi/mfi_linux.c +++ b/sys/dev/mfi/mfi_linux.c @@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$"); #include <sys/module.h> #include <sys/file.h> #include <sys/proc.h> +#include <machine/bus.h> #if defined(__amd64__) /* Assume amd64 wants 32 bit Linux */ #include <machine/../linux32/linux.h> diff --git a/sys/dev/mfi/mfi_pci.c b/sys/dev/mfi/mfi_pci.c index 30db74b..5f757da 100644 --- a/sys/dev/mfi/mfi_pci.c +++ b/sys/dev/mfi/mfi_pci.c @@ -107,7 +107,7 @@ static devclass_t mfi_devclass; DRIVER_MODULE(mfi, pci, mfi_pci_driver, mfi_devclass, 0, 0); MODULE_VERSION(mfi, 1); -static int mfi_msi = 0; +static int mfi_msi = 1; TUNABLE_INT("hw.mfi.msi", &mfi_msi); SYSCTL_INT(_hw_mfi, OID_AUTO, msi, CTLFLAG_RDTUN, &mfi_msi, 0, "Enable use of MSI interrupts"); @@ -120,14 +120,28 @@ struct mfi_ident { int flags; const char *desc; } mfi_identifiers[] = { + {0x1000, 0x005b, 0x1028, 0x1f2d, MFI_FLAGS_SKINNY| MFI_FLAGS_TBOLT, "Dell PERC H810 Adapter"}, + {0x1000, 0x005b, 0x1028, 0x1f30, MFI_FLAGS_SKINNY| MFI_FLAGS_TBOLT, "Dell PERC H710 Embedded"}, + {0x1000, 0x005b, 0x1028, 0x1f31, MFI_FLAGS_SKINNY| MFI_FLAGS_TBOLT, "Dell PERC H710P Adapter"}, + {0x1000, 0x005b, 0x1028, 0x1f33, MFI_FLAGS_SKINNY| MFI_FLAGS_TBOLT, "Dell PERC H710P Mini (blades)"}, + {0x1000, 0x005b, 0x1028, 0x1f34, MFI_FLAGS_SKINNY| MFI_FLAGS_TBOLT, "Dell PERC H710P Mini (monolithics)"}, + {0x1000, 0x005b, 0x1028, 0x1f35, MFI_FLAGS_SKINNY| MFI_FLAGS_TBOLT, "Dell PERC H710 Adapter"}, + {0x1000, 0x005b, 0x1028, 0x1f37, MFI_FLAGS_SKINNY| MFI_FLAGS_TBOLT, "Dell PERC H710 Mini (blades)"}, + {0x1000, 0x005b, 0x1028, 0x1f38, MFI_FLAGS_SKINNY| MFI_FLAGS_TBOLT, "Dell PERC H710 Mini (monolithics)"}, + {0x1000, 0x005b, 0x8086, 0x9265, MFI_FLAGS_SKINNY| MFI_FLAGS_TBOLT, "Intel (R) RAID Controller RS25DB080"}, + {0x1000, 0x005b, 0x8086, 0x9285, MFI_FLAGS_SKINNY| MFI_FLAGS_TBOLT, "Intel (R) RAID Controller RS25NB008"}, + {0x1000, 0x005b, 0xffff, 0xffff, MFI_FLAGS_SKINNY| MFI_FLAGS_TBOLT, "ThunderBolt"}, {0x1000, 0x0060, 0x1028, 0xffff, MFI_FLAGS_1078, "Dell PERC 6"}, {0x1000, 0x0060, 0xffff, 0xffff, MFI_FLAGS_1078, "LSI MegaSAS 1078"}, + {0x1000, 0x0071, 0xffff, 0xffff, MFI_FLAGS_SKINNY, "Drake Skinny"}, + {0x1000, 0x0073, 0xffff, 0xffff, MFI_FLAGS_SKINNY, "Drake Skinny"}, {0x1000, 0x0078, 0xffff, 0xffff, MFI_FLAGS_GEN2, "LSI MegaSAS Gen2"}, {0x1000, 0x0079, 0x1028, 0x1f15, MFI_FLAGS_GEN2, "Dell PERC H800 Adapter"}, {0x1000, 0x0079, 0x1028, 0x1f16, MFI_FLAGS_GEN2, "Dell PERC H700 Adapter"}, {0x1000, 0x0079, 0x1028, 0x1f17, MFI_FLAGS_GEN2, "Dell PERC H700 Integrated"}, {0x1000, 0x0079, 0x1028, 0x1f18, MFI_FLAGS_GEN2, "Dell PERC H700 Modular"}, {0x1000, 0x0079, 0x1028, 0x1f19, MFI_FLAGS_GEN2, "Dell PERC H700"}, + {0x1000, 0x0079, 0x1028, 0x1f1a, MFI_FLAGS_GEN2, "Dell PERC H800 Proto Adapter"}, {0x1000, 0x0079, 0x1028, 0x1f1b, MFI_FLAGS_GEN2, "Dell PERC H800"}, {0x1000, 0x0079, 0x1028, 0xffff, MFI_FLAGS_GEN2, "Dell PERC Gen2"}, {0x1000, 0x0079, 0xffff, 0xffff, MFI_FLAGS_GEN2, "LSI MegaSAS Gen2"}, @@ -201,8 +215,11 @@ mfi_pci_attach(device_t dev) (sc->mfi_flags & MFI_FLAGS_1078)) { /* 1068/1078: Memory mapped BAR is at offset 0x10 */ sc->mfi_regs_rid = PCIR_BAR(0); - } else if (sc->mfi_flags & MFI_FLAGS_GEN2) { - /* GEN2: Memory mapped BAR is at offset 0x14 */ + } + else if ((sc->mfi_flags & MFI_FLAGS_GEN2) || + (sc->mfi_flags & MFI_FLAGS_SKINNY) || + (sc->mfi_flags & MFI_FLAGS_TBOLT)) { + /* Gen2/Skinny: Memory mapped BAR is at offset 0x14 */ sc->mfi_regs_rid = PCIR_BAR(1); } if ((sc->mfi_regs_resource = bus_alloc_resource_any(sc->mfi_dev, @@ -259,8 +276,8 @@ static int mfi_pci_detach(device_t dev) { struct mfi_softc *sc; - struct mfi_disk *ld; - int error; + int error, devcount, i; + device_t *devlist; sc = device_get_softc(dev); @@ -274,13 +291,13 @@ mfi_pci_detach(device_t dev) sc->mfi_detaching = 1; mtx_unlock(&sc->mfi_io_lock); - while ((ld = TAILQ_FIRST(&sc->mfi_ld_tqh)) != NULL) { - if ((error = device_delete_child(dev, ld->ld_dev)) != 0) { - sc->mfi_detaching = 0; - sx_xunlock(&sc->mfi_config_lock); - return (error); - } + if ((error = device_get_children(sc->mfi_dev, &devlist, &devcount)) != 0) { + sx_xunlock(&sc->mfi_config_lock); + return error; } + for (i = 0; i < devcount; i++) + device_delete_child(sc->mfi_dev, devlist[i]); + free(devlist, M_TEMP); sx_xunlock(&sc->mfi_config_lock); EVENTHANDLER_DEREGISTER(shutdown_final, sc->mfi_eh); diff --git a/sys/dev/mfi/mfi_syspd.c b/sys/dev/mfi/mfi_syspd.c new file mode 100644 index 0000000..a8a8b24 --- /dev/null +++ b/sys/dev/mfi/mfi_syspd.c @@ -0,0 +1,276 @@ +/*- + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Copyright 1994-2009 The FreeBSD Project. + * All rights reserved. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD PROJECT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY,OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION)HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies,either expressed or implied, of the FreeBSD Project. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_mfi.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/selinfo.h> +#include <sys/module.h> +#include <sys/malloc.h> +#include <sys/sysctl.h> +#include <sys/uio.h> + +#include <sys/bio.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/disk.h> +#include <geom/geom_disk.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <machine/md_var.h> +#include <machine/bus.h> +#include <sys/rman.h> + +#include <dev/mfi/mfireg.h> +#include <dev/mfi/mfi_ioctl.h> +#include <dev/mfi/mfivar.h> + +static int mfi_syspd_probe(device_t dev); +static int mfi_syspd_attach(device_t dev); +static int mfi_syspd_detach(device_t dev); + +static disk_open_t mfi_syspd_open; +static disk_close_t mfi_syspd_close; +static disk_strategy_t mfi_syspd_strategy; +static dumper_t mfi_syspd_dump; + +static devclass_t mfi_syspd_devclass; + +static device_method_t mfi_syspd_methods[] = { + DEVMETHOD(device_probe, mfi_syspd_probe), + DEVMETHOD(device_attach, mfi_syspd_attach), + DEVMETHOD(device_detach, mfi_syspd_detach), + { 0, 0 } +}; + +static driver_t mfi_syspd_driver = { + "mfisyspd", + mfi_syspd_methods, + sizeof(struct mfi_system_pd) +}; + +DRIVER_MODULE(mfisyspd, mfi, mfi_syspd_driver, mfi_syspd_devclass, 0, 0); + +static int +mfi_syspd_probe(device_t dev) +{ + + return (0); +} + +static int +mfi_syspd_attach(device_t dev) +{ + struct mfi_system_pd *sc; + struct mfi_pd_info *pd_info; + uint64_t sectors; + uint32_t secsize; + + sc = device_get_softc(dev); + pd_info = device_get_ivars(dev); + + sc->pd_dev = dev; + sc->pd_id = pd_info->ref.v.device_id; + sc->pd_unit = device_get_unit(dev); + sc->pd_info = pd_info; + sc->pd_controller = device_get_softc(device_get_parent(dev)); + sc->pd_flags = 0; + + sectors = pd_info->raw_size; + secsize = MFI_SECTOR_LEN; + mtx_lock(&sc->pd_controller->mfi_io_lock); + TAILQ_INSERT_TAIL(&sc->pd_controller->mfi_syspd_tqh, sc, pd_link); + mtx_unlock(&sc->pd_controller->mfi_io_lock); + device_printf(dev, "%juMB (%ju sectors) SYSPD volume\n", + sectors / (1024 * 1024 / secsize), sectors); + sc->pd_disk = disk_alloc(); + sc->pd_disk->d_drv1 = sc; + sc->pd_disk->d_maxsize = sc->pd_controller->mfi_max_io * secsize; + sc->pd_disk->d_name = "mfisyspd"; + sc->pd_disk->d_open = mfi_syspd_open; + sc->pd_disk->d_close = mfi_syspd_close; + sc->pd_disk->d_strategy = mfi_syspd_strategy; + sc->pd_disk->d_dump = mfi_syspd_dump; + sc->pd_disk->d_unit = sc->pd_unit; + sc->pd_disk->d_sectorsize = secsize; + sc->pd_disk->d_mediasize = sectors * secsize; + if (sc->pd_disk->d_mediasize >= (1 * 1024 * 1024)) { + sc->pd_disk->d_fwheads = 255; + sc->pd_disk->d_fwsectors = 63; + } else { + sc->pd_disk->d_fwheads = 64; + sc->pd_disk->d_fwsectors = 32; + } + disk_create(sc->pd_disk, DISK_VERSION); + + device_printf(dev, " SYSPD volume attached\n"); + return (0); +} + +static int +mfi_syspd_detach(device_t dev) +{ + struct mfi_system_pd *sc; + + sc = device_get_softc(dev); + device_printf(dev, "Detaching syspd\n"); + mtx_lock(&sc->pd_controller->mfi_io_lock); + if (((sc->pd_disk->d_flags & DISKFLAG_OPEN) || + (sc->pd_flags & MFI_DISK_FLAGS_OPEN)) && + (sc->pd_controller->mfi_keep_deleted_volumes || + sc->pd_controller->mfi_detaching)) { + mtx_unlock(&sc->pd_controller->mfi_io_lock); + device_printf(dev, "Cant detach syspd\n"); + return (EBUSY); + } + mtx_unlock(&sc->pd_controller->mfi_io_lock); + + disk_destroy(sc->pd_disk); + mtx_lock(&sc->pd_controller->mfi_io_lock); + TAILQ_REMOVE(&sc->pd_controller->mfi_syspd_tqh, sc, pd_link); + mtx_unlock(&sc->pd_controller->mfi_io_lock); + free(sc->pd_info, M_MFIBUF); + return (0); +} + +static int +mfi_syspd_open(struct disk *dp) +{ + struct mfi_system_pd *sc; + int error; + + sc = dp->d_drv1; + mtx_lock(&sc->pd_controller->mfi_io_lock); + if (sc->pd_flags & MFI_DISK_FLAGS_DISABLED) + error = ENXIO; + else { + sc->pd_flags |= MFI_DISK_FLAGS_OPEN; + error = 0; + } + mtx_unlock(&sc->pd_controller->mfi_io_lock); + return (error); +} + +static int +mfi_syspd_close(struct disk *dp) +{ + struct mfi_system_pd *sc; + + sc = dp->d_drv1; + mtx_lock(&sc->pd_controller->mfi_io_lock); + sc->pd_flags &= ~MFI_DISK_FLAGS_OPEN; + mtx_unlock(&sc->pd_controller->mfi_io_lock); + + return (0); +} + +int +mfi_syspd_disable(struct mfi_system_pd *sc) +{ + + device_printf(sc->pd_dev, "syspd disable \n"); + mtx_assert(&sc->pd_controller->mfi_io_lock, MA_OWNED); + if (sc->pd_flags & MFI_DISK_FLAGS_OPEN) { + if (sc->pd_controller->mfi_delete_busy_volumes) + return (0); + device_printf(sc->pd_dev, + "Unable to delete busy syspd device\n"); + return (EBUSY); + } + sc->pd_flags |= MFI_DISK_FLAGS_DISABLED; + return (0); +} + +void +mfi_syspd_enable(struct mfi_system_pd *sc) +{ + + device_printf(sc->pd_dev, "syspd enable \n"); + mtx_assert(&sc->pd_controller->mfi_io_lock, MA_OWNED); + sc->pd_flags &= ~MFI_DISK_FLAGS_DISABLED; +} + +static void +mfi_syspd_strategy(struct bio *bio) +{ + struct mfi_system_pd *sc; + struct mfi_softc *controller; + + sc = bio->bio_disk->d_drv1; + + if (sc == NULL) { + bio->bio_error = EINVAL; + bio->bio_flags |= BIO_ERROR; + bio->bio_resid = bio->bio_bcount; + biodone(bio); + return; + } + + controller = sc->pd_controller; + bio->bio_driver1 = (void *)(uintptr_t)sc->pd_id; + /* Mark it as system PD IO */ + bio->bio_driver2 = (void *)MFI_SYS_PD_IO; + mtx_lock(&controller->mfi_io_lock); + mfi_enqueue_bio(controller, bio); + mfi_startio(controller); + mtx_unlock(&controller->mfi_io_lock); + return; +} + +static int +mfi_syspd_dump(void *arg, void *virt, vm_offset_t phys, off_t offset, + size_t len) +{ + struct mfi_system_pd *sc; + struct mfi_softc *parent_sc; + struct disk *dp; + int error; + + dp = arg; + sc = dp->d_drv1; + parent_sc = sc->pd_controller; + + if (len > 0) { + if ((error = mfi_dump_syspd_blocks(parent_sc, + sc->pd_id, offset / MFI_SECTOR_LEN, virt, len)) != 0) + return (error); + } else { + /* mfi_sync_cache(parent_sc, sc->ld_id); */ + } + return (0); +} diff --git a/sys/dev/mfi/mfi_tbolt.c b/sys/dev/mfi/mfi_tbolt.c new file mode 100644 index 0000000..aa9fb77 --- /dev/null +++ b/sys/dev/mfi/mfi_tbolt.c @@ -0,0 +1,1345 @@ + /*- + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Copyright 1994-2009 The FreeBSD Project. + * All rights reserved. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD PROJECT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY,OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION)HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies,either expressed or implied, of the FreeBSD Project. + */ + + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_mfi.h" + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/kernel.h> +#include <sys/selinfo.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/bio.h> +#include <sys/ioccom.h> +#include <sys/eventhandler.h> +#include <sys/callout.h> +#include <sys/uio.h> +#include <machine/bus.h> +#include <sys/sysctl.h> +#include <sys/systm.h> +#include <sys/malloc.h> + +#include <dev/mfi/mfireg.h> +#include <dev/mfi/mfi_ioctl.h> +#include <dev/mfi/mfivar.h> + +struct mfi_cmd_tbolt *mfi_tbolt_get_cmd(struct mfi_softc *sc); +union mfi_mpi2_request_descriptor * +mfi_tbolt_get_request_descriptor(struct mfi_softc *sc, uint16_t index); +void mfi_tbolt_complete_cmd(struct mfi_softc *sc); +int mfi_tbolt_build_io(struct mfi_softc *sc, struct mfi_command *mfi_cmd, + struct mfi_cmd_tbolt *cmd); +static inline void mfi_tbolt_return_cmd(struct mfi_softc *sc, + struct mfi_cmd_tbolt *cmd); +union mfi_mpi2_request_descriptor *mfi_tbolt_build_mpt_cmd(struct mfi_softc + *sc, struct mfi_command *cmd); +uint8_t +mfi_build_mpt_pass_thru(struct mfi_softc *sc, struct mfi_command *mfi_cmd); +union mfi_mpi2_request_descriptor *mfi_build_and_issue_cmd(struct mfi_softc + *sc, struct mfi_command *mfi_cmd); +int mfi_tbolt_is_ldio(struct mfi_command *mfi_cmd); +void mfi_tbolt_build_ldio(struct mfi_softc *sc, struct mfi_command *mfi_cmd, + struct mfi_cmd_tbolt *cmd); +static int mfi_tbolt_make_sgl(struct mfi_softc *sc, struct mfi_command + *mfi_cmd, pMpi25IeeeSgeChain64_t sgl_ptr, struct mfi_cmd_tbolt *cmd); +static int mfi_tbolt_build_cdb(struct mfi_softc *sc, struct mfi_command + *mfi_cmd, uint8_t *cdb); +void +map_tbolt_cmd_status(struct mfi_command *mfi_cmd, uint8_t status, + uint8_t ext_status); +static void mfi_issue_pending_cmds_again (struct mfi_softc *sc); +static void mfi_kill_hba (struct mfi_softc *sc); +static void mfi_process_fw_state_chg_isr(void *arg); +uint8_t mfi_tbolt_get_map_info(struct mfi_softc *sc); + +#define MFI_FUSION_ENABLE_INTERRUPT_MASK (0x00000008) + +void +mfi_tbolt_enable_intr_ppc(struct mfi_softc *sc) +{ + MFI_WRITE4(sc, MFI_OMSK, ~MFI_FUSION_ENABLE_INTERRUPT_MASK); + MFI_READ4(sc, MFI_OMSK); +} + +void +mfi_tbolt_disable_intr_ppc(struct mfi_softc *sc) +{ + MFI_WRITE4(sc, MFI_OMSK, 0xFFFFFFFF); + MFI_READ4(sc, MFI_OMSK); +} + +int32_t +mfi_tbolt_read_fw_status_ppc(struct mfi_softc *sc) +{ + return MFI_READ4(sc, MFI_OSP0); +} + +int32_t +mfi_tbolt_check_clear_intr_ppc(struct mfi_softc *sc) +{ + int32_t status, mfi_status = 0; + + status = MFI_READ4(sc, MFI_OSTS); + + if (status & 1) { + MFI_WRITE4(sc, MFI_OSTS, status); + MFI_READ4(sc, MFI_OSTS); + if (status & MFI_STATE_CHANGE_INTERRUPT) { + mfi_status |= MFI_FIRMWARE_STATE_CHANGE; + } + + return mfi_status; + } + if (!(status & MFI_FUSION_ENABLE_INTERRUPT_MASK)) + return 1; + + MFI_READ4(sc, MFI_OSTS); + return 0; +} + + +void +mfi_tbolt_issue_cmd_ppc(struct mfi_softc *sc, bus_addr_t bus_add, + uint32_t frame_cnt) +{ + bus_add |= (MFI_REQ_DESCRIPT_FLAGS_MFA + << MFI_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); + MFI_WRITE4(sc, MFI_IQPL, (uint32_t)bus_add); + MFI_WRITE4(sc, MFI_IQPH, (uint32_t)((uint64_t)bus_add >> 32)); +} + +/** + * mfi_tbolt_adp_reset - For controller reset + * @regs: MFI register set + */ +int mfi_tbolt_adp_reset(struct mfi_softc *sc) +{ + int retry = 0, i = 0; + int HostDiag; + + MFI_WRITE4(sc, MFI_WSR, 0xF); + MFI_WRITE4(sc, MFI_WSR, 4); + MFI_WRITE4(sc, MFI_WSR, 0xB); + MFI_WRITE4(sc, MFI_WSR, 2); + MFI_WRITE4(sc, MFI_WSR, 7); + MFI_WRITE4(sc, MFI_WSR, 0xD); + + for (i = 0; i < 10000; i++) ; + + HostDiag = (uint32_t)MFI_READ4(sc, MFI_HDR); + + while (!( HostDiag & DIAG_WRITE_ENABLE)) { + for (i = 0; i < 1000; i++); + HostDiag = (uint32_t)MFI_READ4(sc, MFI_HDR); + device_printf(sc->mfi_dev, "ADP_RESET_TBOLT: retry time=%x, " + "hostdiag=%x\n", retry, HostDiag); + + if (retry++ >= 100) + return 1; + } + + device_printf(sc->mfi_dev, "ADP_RESET_TBOLT: HostDiag=%x\n", HostDiag); + + MFI_WRITE4(sc, MFI_HDR, (HostDiag | DIAG_RESET_ADAPTER)); + + for (i=0; i < 10; i++) { + for (i = 0; i < 10000; i++); + } + + HostDiag = (uint32_t)MFI_READ4(sc, MFI_RSR); + while (HostDiag & DIAG_RESET_ADAPTER) { + for (i = 0; i < 1000; i++) ; + HostDiag = (uint32_t)MFI_READ4(sc, MFI_RSR); + device_printf(sc->mfi_dev, "ADP_RESET_TBOLT: retry time=%x, " + "hostdiag=%x\n", retry, HostDiag); + + if (retry++ >= 1000) + return 1; + } + return 0; +} + +/* + ******************************************************************************************* + * Description: + * This routine initialize Thunderbolt specific device information + ******************************************************************************************* + */ +void mfi_tbolt_init_globals(struct mfi_softc *sc) +{ + /* Initialize single reply size and Message size */ + sc->reply_size = MEGASAS_THUNDERBOLT_REPLY_SIZE; + sc->raid_io_msg_size = MEGASAS_THUNDERBOLT_NEW_MSG_SIZE; + + /* + * Calculating how many SGEs allowed in a allocated main message + * (size of the Message - Raid SCSI IO message size(except SGE)) + * / size of SGE + * (0x100 - (0x90 - 0x10)) / 0x10 = 8 + */ + sc->max_SGEs_in_main_message = + (uint8_t)((sc->raid_io_msg_size + - (sizeof(struct mfi_mpi2_request_raid_scsi_io) + - sizeof(MPI2_SGE_IO_UNION))) / sizeof(MPI2_SGE_IO_UNION)); + /* + * (Command frame size allocaed in SRB ext - Raid SCSI IO message size) + * / size of SGL ; + * (1280 - 256) / 16 = 64 + */ + sc->max_SGEs_in_chain_message = (MR_COMMAND_SIZE + - sc->raid_io_msg_size) / sizeof(MPI2_SGE_IO_UNION); + /* + * (0x08-1) + 0x40 = 0x47 - 0x01 = 0x46 one is left for command + * colscing + */ + sc->mfi_max_sge = (sc->max_SGEs_in_main_message - 1) + + sc->max_SGEs_in_chain_message - 1; + /* + * This is the offset in number of 4 * 32bit words to the next chain + * (0x100 - 0x10)/0x10 = 0xF(15) + */ + sc->chain_offset_value_for_main_message = (sc->raid_io_msg_size + - sizeof(MPI2_SGE_IO_UNION))/16; + sc->chain_offset_value_for_mpt_ptmsg + = offsetof(struct mfi_mpi2_request_raid_scsi_io, SGL)/16; + sc->mfi_cmd_pool_tbolt = NULL; + sc->request_desc_pool = NULL; +} + +/* + **************************************************************************** + * Description: + * This function calculates the memory requirement for Thunderbolt + * controller + * Return Value: + * Total required memory in bytes + **************************************************************************** + */ + +uint32_t mfi_tbolt_get_memory_requirement(struct mfi_softc *sc) +{ + uint32_t size; + size = MEGASAS_THUNDERBOLT_MSG_ALLIGNMENT; /* for Alignment */ + size += sc->raid_io_msg_size * (sc->mfi_max_fw_cmds + 1); + size += sc->reply_size * sc->mfi_max_fw_cmds; + /* this is for SGL's */ + size += MEGASAS_MAX_SZ_CHAIN_FRAME * sc->mfi_max_fw_cmds; + return size; +} + +/* + **************************************************************************** + * Description: + * This function will prepare message pools for the Thunderbolt controller + * Arguments: + * DevExt - HBA miniport driver's adapter data storage structure + * pMemLocation - start of the memory allocated for Thunderbolt. + * Return Value: + * TRUE if successful + * FALSE if failed + **************************************************************************** + */ +int mfi_tbolt_init_desc_pool(struct mfi_softc *sc, uint8_t* mem_location, + uint32_t tbolt_contg_length) +{ + uint32_t offset = 0; + uint8_t *addr = mem_location; + + /* Request Descriptor Base physical Address */ + + /* For Request Decriptors Virtual Memory */ + /* Initialise the aligned IO Frames Virtual Memory Pointer */ + if (((uintptr_t)addr) & (0xFF)) { + addr = &addr[sc->raid_io_msg_size]; + addr = (uint8_t *)((uintptr_t)addr & (~0xFF)); + sc->request_message_pool_align = addr; + } else + sc->request_message_pool_align = addr; + + offset = sc->request_message_pool_align - sc->request_message_pool; + sc->request_msg_busaddr = sc->mfi_tb_busaddr + offset; + + /* DJA XXX should this be bus dma ??? */ + /* Skip request message pool */ + addr = &addr[sc->raid_io_msg_size * (sc->mfi_max_fw_cmds + 1)]; + /* Reply Frame Pool is initialized */ + sc->reply_frame_pool = (struct mfi_mpi2_reply_header *) addr; + if (((uintptr_t)addr) & (0xFF)) { + addr = &addr[sc->reply_size]; + addr = (uint8_t *)((uintptr_t)addr & (~0xFF)); + } + sc->reply_frame_pool_align + = (struct mfi_mpi2_reply_header *)addr; + + offset = (uintptr_t)sc->reply_frame_pool_align + - (uintptr_t)sc->request_message_pool; + sc->reply_frame_busaddr = sc->mfi_tb_busaddr + offset; + + /* Skip Reply Frame Pool */ + addr += sc->reply_size * sc->mfi_max_fw_cmds; + sc->reply_pool_limit = addr; + + /* initializing reply address to 0xFFFFFFFF */ + memset((uint8_t *)sc->reply_frame_pool, 0xFF, + (sc->reply_size * sc->mfi_max_fw_cmds)); + + offset = sc->reply_size * sc->mfi_max_fw_cmds; + sc->sg_frame_busaddr = sc->reply_frame_busaddr + offset; + /* initialize the last_reply_idx to 0 */ + sc->last_reply_idx = 0; + offset = (sc->sg_frame_busaddr + (MEGASAS_MAX_SZ_CHAIN_FRAME * + sc->mfi_max_fw_cmds)) - sc->mfi_tb_busaddr; + if (offset > tbolt_contg_length) + device_printf(sc->mfi_dev, "Error:Initialized more than " + "allocated\n"); + return 0; +} + +/* + **************************************************************************** + * Description: + * This routine prepare and issue INIT2 frame to the Firmware + **************************************************************************** + */ + +int +mfi_tbolt_init_MFI_queue(struct mfi_softc *sc) +{ + struct MPI2_IOC_INIT_REQUEST *mpi2IocInit; + struct mfi_init_frame *mfi_init; + uintptr_t offset = 0; + bus_addr_t phyAddress; + MFI_ADDRESS *mfiAddressTemp; + struct mfi_command *cm; + int error; + + mpi2IocInit = (struct MPI2_IOC_INIT_REQUEST *)sc->mfi_tb_ioc_init_desc; + /* Check if initialization is already completed */ + if (sc->MFA_enabled) { + return 1; + } + + mtx_lock(&sc->mfi_io_lock); + if ((cm = mfi_dequeue_free(sc)) == NULL) { + mtx_unlock(&sc->mfi_io_lock); + return (EBUSY); + } + cm->cm_frame = (union mfi_frame *)((uintptr_t)sc->mfi_tb_init); + cm->cm_frame_busaddr = sc->mfi_tb_init_busaddr; + cm->cm_dmamap = sc->mfi_tb_init_dmamap; + cm->cm_frame->header.context = 0; + cm->cm_sc = sc; + cm->cm_index = 0; + + /* + * Abuse the SG list area of the frame to hold the init_qinfo + * object; + */ + mfi_init = &cm->cm_frame->init; + + bzero(mpi2IocInit, sizeof(struct MPI2_IOC_INIT_REQUEST)); + mpi2IocInit->Function = MPI2_FUNCTION_IOC_INIT; + mpi2IocInit->WhoInit = MPI2_WHOINIT_HOST_DRIVER; + + /* set MsgVersion and HeaderVersion host driver was built with */ + mpi2IocInit->MsgVersion = MPI2_VERSION; + mpi2IocInit->HeaderVersion = MPI2_HEADER_VERSION; + mpi2IocInit->SystemRequestFrameSize = sc->raid_io_msg_size/4; + mpi2IocInit->ReplyDescriptorPostQueueDepth + = (uint16_t)sc->mfi_max_fw_cmds; + mpi2IocInit->ReplyFreeQueueDepth = 0; /* Not supported by MR. */ + + /* Get physical address of reply frame pool */ + offset = (uintptr_t) sc->reply_frame_pool_align + - (uintptr_t)sc->request_message_pool; + phyAddress = sc->mfi_tb_busaddr + offset; + mfiAddressTemp = + (MFI_ADDRESS *)&mpi2IocInit->ReplyDescriptorPostQueueAddress; + mfiAddressTemp->u.addressLow = (uint32_t)phyAddress; + mfiAddressTemp->u.addressHigh = (uint32_t)((uint64_t)phyAddress >> 32); + + /* Get physical address of request message pool */ + offset = sc->request_message_pool_align - sc->request_message_pool; + phyAddress = sc->mfi_tb_busaddr + offset; + mfiAddressTemp = (MFI_ADDRESS *)&mpi2IocInit->SystemRequestFrameBaseAddress; + mfiAddressTemp->u.addressLow = (uint32_t)phyAddress; + mfiAddressTemp->u.addressHigh = (uint32_t)((uint64_t)phyAddress >> 32); + mpi2IocInit->ReplyFreeQueueAddress = 0; /* Not supported by MR. */ + mpi2IocInit->TimeStamp = time_uptime; + + if (sc->verbuf) { + snprintf((char *)sc->verbuf, strlen(MEGASAS_VERSION) + 2, "%s\n", + MEGASAS_VERSION); + mfi_init->driver_ver_lo = (uint32_t)sc->verbuf_h_busaddr; + mfi_init->driver_ver_hi = + (uint32_t)((uint64_t)sc->verbuf_h_busaddr >> 32); + } + /* Get the physical address of the mpi2 ioc init command */ + phyAddress = sc->mfi_tb_ioc_init_busaddr; + mfi_init->qinfo_new_addr_lo = (uint32_t)phyAddress; + mfi_init->qinfo_new_addr_hi = (uint32_t)((uint64_t)phyAddress >> 32); + mfi_init->header.flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE; + + mfi_init->header.cmd = MFI_CMD_INIT; + mfi_init->header.data_len = sizeof(struct MPI2_IOC_INIT_REQUEST); + mfi_init->header.cmd_status = MFI_STAT_INVALID_STATUS; + + cm->cm_data = NULL; + cm->cm_flags |= MFI_CMD_POLLED; + cm->cm_timestamp = time_uptime; + if ((error = mfi_mapcmd(sc, cm)) != 0) { + device_printf(sc->mfi_dev, "failed to send IOC init2 " + "command %d at %lx\n", error, (long)cm->cm_frame_busaddr); + mfi_release_command(cm); + mtx_unlock(&sc->mfi_io_lock); + return (error); + } + mfi_release_command(cm); + mtx_unlock(&sc->mfi_io_lock); + + if (mfi_init->header.cmd_status == 0) { + sc->MFA_enabled = 1; + } + else { + device_printf(sc->mfi_dev, "Init command Failed %x\n", + mfi_init->header.cmd_status); + return 1; + } + + return 0; + +} + +int mfi_tbolt_alloc_cmd(struct mfi_softc *sc) +{ + struct mfi_cmd_tbolt *cmd; + bus_addr_t io_req_base_phys; + uint8_t *io_req_base; + int i = 0, j = 0, offset = 0; + + /* + * sc->mfi_cmd_pool_tbolt is an array of struct mfi_cmd_tbolt pointers. + * Allocate the dynamic array first and then allocate individual + * commands. + */ + sc->request_desc_pool = malloc(sizeof( + union mfi_mpi2_request_descriptor) * sc->mfi_max_fw_cmds, + M_MFIBUF, M_NOWAIT|M_ZERO); + sc->mfi_cmd_pool_tbolt = malloc(sizeof(struct mfi_cmd_tbolt*) + * sc->mfi_max_fw_cmds, M_MFIBUF, M_NOWAIT|M_ZERO); + + if (!sc->mfi_cmd_pool_tbolt) { + device_printf(sc->mfi_dev, "out of memory. Could not alloc " + "memory for cmd_list_fusion\n"); + return 1; + } + + for (i = 0; i < sc->mfi_max_fw_cmds; i++) { + sc->mfi_cmd_pool_tbolt[i] = malloc(sizeof( + struct mfi_cmd_tbolt),M_MFIBUF, M_NOWAIT|M_ZERO); + + if (!sc->mfi_cmd_pool_tbolt[i]) { + device_printf(sc->mfi_dev, "Could not alloc cmd list " + "fusion\n"); + + for (j = 0; j < i; j++) + free(sc->mfi_cmd_pool_tbolt[j], M_MFIBUF); + + free(sc->mfi_cmd_pool_tbolt, M_MFIBUF); + sc->mfi_cmd_pool_tbolt = NULL; + } + } + + /* + * The first 256 bytes (SMID 0) is not used. Don't add to the cmd + *list + */ + io_req_base = sc->request_message_pool_align + + MEGASAS_THUNDERBOLT_NEW_MSG_SIZE; + io_req_base_phys = sc->request_msg_busaddr + + MEGASAS_THUNDERBOLT_NEW_MSG_SIZE; + + /* + * Add all the commands to command pool (instance->cmd_pool) + */ + /* SMID 0 is reserved. Set SMID/index from 1 */ + + for (i = 0; i < sc->mfi_max_fw_cmds; i++) { + cmd = sc->mfi_cmd_pool_tbolt[i]; + offset = MEGASAS_THUNDERBOLT_NEW_MSG_SIZE * i; + cmd->index = i + 1; + cmd->request_desc = (union mfi_mpi2_request_descriptor *) + (sc->request_desc_pool + i); + cmd->io_request = (struct mfi_mpi2_request_raid_scsi_io *) + (io_req_base + offset); + cmd->io_request_phys_addr = io_req_base_phys + offset; + cmd->sg_frame = (MPI2_SGE_IO_UNION *)(sc->reply_pool_limit + + i * MEGASAS_MAX_SZ_CHAIN_FRAME); + cmd->sg_frame_phys_addr = sc->sg_frame_busaddr + i + * MEGASAS_MAX_SZ_CHAIN_FRAME; + + TAILQ_INSERT_TAIL(&(sc->mfi_cmd_tbolt_tqh), cmd, next); + } + return 0; +} + +int mfi_tbolt_reset(struct mfi_softc *sc) +{ + uint32_t fw_state; + + mtx_lock(&sc->mfi_io_lock); + if (atomic_read(&sc->fw_reset_no_pci_access)) { + device_printf(sc->mfi_dev, "NO PCI ACCESS\n"); + mtx_unlock(&sc->mfi_io_lock); + return 1; + } + + if (sc->hw_crit_error) { + device_printf(sc->mfi_dev, "HW CRITICAL ERROR\n"); + mtx_unlock(&sc->mfi_io_lock); + return 1; + } + + if (sc->mfi_flags & MFI_FLAGS_TBOLT) { + fw_state = sc->mfi_read_fw_status(sc); + if ((fw_state & MFI_FWSTATE_FAULT) == MFI_FWSTATE_FAULT) { + if ((sc->disableOnlineCtrlReset == 0) + && (sc->adpreset == 0)) { + device_printf(sc->mfi_dev, "Adapter RESET " + "condition is detected\n"); + sc->adpreset = 1; + sc->issuepend_done = 0; + sc->MFA_enabled = 0; + sc->last_reply_idx = 0; + mfi_process_fw_state_chg_isr((void *) sc); + } + mtx_unlock(&sc->mfi_io_lock); + return 0; + } + } + mtx_unlock(&sc->mfi_io_lock); + return 1; +} + +/* + * mfi_intr_tbolt - isr entry point + */ +void mfi_intr_tbolt(void *arg) +{ + struct mfi_softc *sc = (struct mfi_softc *)arg; + + if (sc->mfi_check_clear_intr(sc) == 1) { + return; + } + if (sc->mfi_detaching) + return; + mtx_lock(&sc->mfi_io_lock); + mfi_tbolt_complete_cmd(sc); + if (sc->mfi_flags & MFI_FLAGS_QFRZN) + sc->mfi_flags &= ~MFI_FLAGS_QFRZN; + mfi_startio(sc); + mtx_unlock(&sc->mfi_io_lock); + return; +} + +/** + * map_cmd_status - Maps FW cmd status to OS cmd status + * @cmd : Pointer to cmd + * @status : status of cmd returned by FW + * @ext_status : ext status of cmd returned by FW + */ + +void +map_tbolt_cmd_status(struct mfi_command *mfi_cmd, uint8_t status, + uint8_t ext_status) +{ + + switch (status) { + + case MFI_STAT_OK: + mfi_cmd->cm_frame->header.cmd_status = 0; + mfi_cmd->cm_frame->dcmd.header.cmd_status = 0; + break; + + case MFI_STAT_SCSI_IO_FAILED: + case MFI_STAT_LD_INIT_IN_PROGRESS: + mfi_cmd->cm_frame->header.cmd_status = status; + mfi_cmd->cm_frame->header.scsi_status = ext_status; + mfi_cmd->cm_frame->dcmd.header.cmd_status = status; + mfi_cmd->cm_frame->dcmd.header.scsi_status + = ext_status; + break; + + case MFI_STAT_SCSI_DONE_WITH_ERROR: + mfi_cmd->cm_frame->header.cmd_status = ext_status; + mfi_cmd->cm_frame->dcmd.header.cmd_status = ext_status; + break; + + case MFI_STAT_LD_OFFLINE: + case MFI_STAT_DEVICE_NOT_FOUND: + mfi_cmd->cm_frame->header.cmd_status = status; + mfi_cmd->cm_frame->dcmd.header.cmd_status = status; + break; + + default: + mfi_cmd->cm_frame->header.cmd_status = status; + mfi_cmd->cm_frame->dcmd.header.cmd_status = status; + break; + } +} + +/** + * mfi_tbolt_return_cmd - Return a cmd to free command pool + * @instance: Adapter soft state + * @cmd: Command packet to be returned to free command pool + */ +static inline void +mfi_tbolt_return_cmd(struct mfi_softc *sc, struct mfi_cmd_tbolt *cmd) +{ + mtx_assert(&sc->mfi_io_lock, MA_OWNED); + + TAILQ_INSERT_TAIL(&sc->mfi_cmd_tbolt_tqh, cmd, next); +} + +void mfi_tbolt_complete_cmd(struct mfi_softc *sc) +{ + struct mfi_mpi2_reply_header *desc, *reply_desc; + struct mfi_command *cmd_mfi; /* For MFA Cmds */ + struct mfi_cmd_tbolt *cmd_tbolt; + uint16_t smid; + uint8_t reply_descript_type; + struct mfi_mpi2_request_raid_scsi_io *scsi_io_req; + uint32_t status, extStatus; + uint16_t num_completed; + union desc_value val; + + desc = (struct mfi_mpi2_reply_header *) + ((uintptr_t)sc->reply_frame_pool_align + + sc->last_reply_idx * sc->reply_size); + reply_desc = desc; + + if (!reply_desc) + device_printf(sc->mfi_dev, "reply desc is NULL!!\n"); + + reply_descript_type = reply_desc->ReplyFlags + & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; + if (reply_descript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) + return; + + num_completed = 0; + val.word = ((union mfi_mpi2_reply_descriptor *)desc)->words; + + /* Read Reply descriptor */ + while ((val.u.low != 0xFFFFFFFF) && (val.u.high != 0xFFFFFFFF)) { + + smid = reply_desc->SMID; + if (!smid || smid > sc->mfi_max_fw_cmds + 1) { + device_printf(sc->mfi_dev, "smid is %x. Cannot " + "proceed. Returning \n", smid); + return; + } + + cmd_tbolt = sc->mfi_cmd_pool_tbolt[smid - 1]; + cmd_mfi = &sc->mfi_commands[cmd_tbolt->sync_cmd_idx]; + scsi_io_req = cmd_tbolt->io_request; + + /* Check if internal commands */ + status = cmd_mfi->cm_frame->dcmd.header.cmd_status; + extStatus = cmd_mfi->cm_frame->dcmd.header.scsi_status; + + switch (scsi_io_req->Function) { + case MPI2_FUNCTION_LD_IO_REQUEST: + /* Regular Path IO. */ + /* Map the Fw Error Status. */ + map_tbolt_cmd_status(cmd_mfi, status, + extStatus); + if ((cmd_mfi->cm_frame->dcmd.opcode + == MFI_DCMD_LD_MAP_GET_INFO) + && (cmd_mfi->cm_frame->dcmd.mbox[1] == 1)) { + if (cmd_mfi->cm_frame->header.cmd_status + != 0) + device_printf(sc->mfi_dev, + "map sync failed\n"); + else { + sc->map_id++; + device_printf(sc->mfi_dev, + "map sync completed\n"); + mfi_release_command(cmd_mfi); + } + } + if ((cmd_mfi->cm_flags & MFI_ON_MFIQ_BUSY) + == MFI_ON_MFIQ_BUSY + && (cmd_mfi->cm_flags & MFI_CMD_POLLED) == 0) { + /* BHARAT poll workaround */ + mfi_remove_busy(cmd_mfi); + cmd_mfi->cm_error = 0; + mfi_complete(sc, cmd_mfi); + } + mfi_tbolt_return_cmd(sc, cmd_tbolt); + break; + case MPI2_FUNCTION_PASSTHRU_IO_REQUEST: + map_tbolt_cmd_status(cmd_mfi, status, extStatus); + if ((cmd_mfi->cm_frame->dcmd.opcode + == MFI_DCMD_LD_MAP_GET_INFO) + && (cmd_mfi->cm_frame->dcmd.mbox[1] == 1)) { + if (cmd_mfi->cm_frame->header.cmd_status != 0) + device_printf(sc->mfi_dev, + "map sync failed\n"); + else { + sc->map_id++; + device_printf(sc->mfi_dev, + "map sync completed\n"); + mfi_release_command(cmd_mfi); + } + } + if ((cmd_mfi->cm_flags & MFI_ON_MFIQ_BUSY) + == MFI_ON_MFIQ_BUSY + && (cmd_mfi->cm_flags & MFI_CMD_POLLED) == 0) { + /* BHARAT poll workaround */ + mfi_remove_busy(cmd_mfi); + cmd_mfi->cm_error = 0; + mfi_complete(sc, cmd_mfi); + } + mfi_tbolt_return_cmd(sc, cmd_tbolt); + break; + } + + sc->last_reply_idx++; + if (sc->last_reply_idx >= sc->mfi_max_fw_cmds) { + MFI_WRITE4(sc, MFI_RPI, sc->last_reply_idx); + sc->last_reply_idx = 0; + } + /*set it back to all 0xfff.*/ + ((union mfi_mpi2_reply_descriptor*)desc)->words = + ~((uint64_t)0x00); + + num_completed++; + + /* Get the next reply descriptor */ + desc = (struct mfi_mpi2_reply_header *) + ((uintptr_t)sc->reply_frame_pool_align + + sc->last_reply_idx * sc->reply_size); + reply_desc = desc; + val.word = ((union mfi_mpi2_reply_descriptor*)desc)->words; + reply_descript_type = reply_desc->ReplyFlags + & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; + if (reply_descript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) + break; + } + + if (!num_completed) + return; + + /* update replyIndex to FW */ + if (sc->last_reply_idx) + MFI_WRITE4(sc, MFI_RPI, sc->last_reply_idx); + + return; +} + +/** + * mfi_get_cmd - Get a command from the free pool + * @instance: Adapter soft state + * + * Returns a free command from the pool + */ + +struct mfi_cmd_tbolt *mfi_tbolt_get_cmd(struct mfi_softc + *sc) +{ + struct mfi_cmd_tbolt *cmd = NULL; + + mtx_assert(&sc->mfi_io_lock, MA_OWNED); + + cmd = TAILQ_FIRST(&sc->mfi_cmd_tbolt_tqh); + TAILQ_REMOVE(&sc->mfi_cmd_tbolt_tqh, cmd, next); + memset((uint8_t *)cmd->sg_frame, 0, MEGASAS_MAX_SZ_CHAIN_FRAME); + memset((uint8_t *)cmd->io_request, 0, + MEGASAS_THUNDERBOLT_NEW_MSG_SIZE); + return cmd; +} + +union mfi_mpi2_request_descriptor * +mfi_tbolt_get_request_descriptor(struct mfi_softc *sc, uint16_t index) +{ + uint8_t *p; + + if (index >= sc->mfi_max_fw_cmds) { + device_printf(sc->mfi_dev, "Invalid SMID (0x%x)request " + "for descriptor\n", index); + return NULL; + } + p = sc->request_desc_pool + sizeof(union mfi_mpi2_request_descriptor) + * index; + memset(p, 0, sizeof(union mfi_mpi2_request_descriptor)); + return (union mfi_mpi2_request_descriptor *)p; +} + + +/* Used to build IOCTL cmd */ +uint8_t +mfi_build_mpt_pass_thru(struct mfi_softc *sc, struct mfi_command *mfi_cmd) +{ + MPI25_IEEE_SGE_CHAIN64 *mpi25_ieee_chain; + struct mfi_mpi2_request_raid_scsi_io *io_req; + struct mfi_cmd_tbolt *cmd; + + cmd = mfi_tbolt_get_cmd(sc); + if (!cmd) + return EBUSY; + mfi_cmd->cm_extra_frames = cmd->index; /* Frame count used as SMID */ + cmd->sync_cmd_idx = mfi_cmd->cm_index; + io_req = cmd->io_request; + mpi25_ieee_chain = (MPI25_IEEE_SGE_CHAIN64 *)&io_req->SGL.IeeeChain; + + io_req->Function = MPI2_FUNCTION_PASSTHRU_IO_REQUEST; + io_req->SGLOffset0 = offsetof(struct mfi_mpi2_request_raid_scsi_io, + SGL) / 4; + io_req->ChainOffset = sc->chain_offset_value_for_mpt_ptmsg; + + mpi25_ieee_chain->Address = mfi_cmd->cm_frame_busaddr; + + /* + In MFI pass thru, nextChainOffset will always be zero to + indicate the end of the chain. + */ + mpi25_ieee_chain->Flags= MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT + | MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR; + + /* setting the length to the maximum length */ + mpi25_ieee_chain->Length = 1024; + + return 0; +} + +void +mfi_tbolt_build_ldio(struct mfi_softc *sc, struct mfi_command *mfi_cmd, + struct mfi_cmd_tbolt *cmd) +{ + uint32_t start_lba_lo = 0, start_lba_hi = 0, device_id; + struct mfi_mpi2_request_raid_scsi_io *io_request; + struct IO_REQUEST_INFO io_info; + + device_id = mfi_cmd->cm_frame->io.header.target_id; + io_request = cmd->io_request; + io_request->RaidContext.TargetID = device_id; + io_request->RaidContext.Status = 0; + io_request->RaidContext.exStatus =0; + + start_lba_lo = mfi_cmd->cm_frame->io.lba_lo; + start_lba_hi = mfi_cmd->cm_frame->io.lba_hi; + + memset(&io_info, 0, sizeof(struct IO_REQUEST_INFO)); + io_info.ldStartBlock = ((uint64_t)start_lba_hi << 32) | start_lba_lo; + io_info.numBlocks = mfi_cmd->cm_frame->io.header.data_len; + io_info.ldTgtId = device_id; + if ((mfi_cmd->cm_frame->header.flags & MFI_FRAME_DIR_READ) == + MFI_FRAME_DIR_READ) + io_info.isRead = 1; + + io_request->RaidContext.timeoutValue + = MFI_FUSION_FP_DEFAULT_TIMEOUT; + io_request->Function = MPI2_FUNCTION_LD_IO_REQUEST; + io_request->DevHandle = device_id; + cmd->request_desc->header.RequestFlags + = (MFI_REQ_DESCRIPT_FLAGS_LD_IO + << MFI_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); + if ((io_request->IoFlags == 6) && (io_info.numBlocks == 0)) + io_request->RaidContext.RegLockLength = 0x100; + io_request->DataLength = mfi_cmd->cm_frame->io.header.data_len + * MFI_SECTOR_LEN; +} + +int mfi_tbolt_is_ldio(struct mfi_command *mfi_cmd) +{ + if (mfi_cmd->cm_frame->header.cmd == MFI_CMD_LD_READ + || mfi_cmd->cm_frame->header.cmd == MFI_CMD_LD_WRITE) + return 1; + else + return 0; +} + +int +mfi_tbolt_build_io(struct mfi_softc *sc, struct mfi_command *mfi_cmd, struct mfi_cmd_tbolt *cmd) +{ + uint32_t device_id; + uint32_t sge_count; + uint8_t cdb[32], cdb_len; + + memset(cdb, 0, 32); + struct mfi_mpi2_request_raid_scsi_io *io_request = cmd->io_request; + + device_id = mfi_cmd->cm_frame->header.target_id; + + /* Have to build CDB here for TB as BSD don't have a scsi layer */ + if ((cdb_len = mfi_tbolt_build_cdb(sc, mfi_cmd, cdb)) == 1) + return 1; + + /* Just the CDB length,rest of the Flags are zero */ + io_request->IoFlags = cdb_len; + memcpy(io_request->CDB.CDB32, cdb, 32); + + if (mfi_tbolt_is_ldio(mfi_cmd)) + mfi_tbolt_build_ldio(sc, mfi_cmd , cmd); + else + return 1; + + /* + * Construct SGL + */ + sge_count = mfi_tbolt_make_sgl(sc, mfi_cmd, + (pMpi25IeeeSgeChain64_t) &io_request->SGL, cmd); + if (sge_count > sc->mfi_max_sge) { + device_printf(sc->mfi_dev, "Error. sge_count (0x%x) exceeds " + "max (0x%x) allowed\n", sge_count, sc->mfi_max_sge); + return 1; + } + io_request->RaidContext.numSGE = sge_count; + io_request->SGLFlags = MPI2_SGE_FLAGS_64_BIT_ADDRESSING; + + if (mfi_cmd->cm_frame->header.cmd == MFI_CMD_LD_WRITE) + io_request->Control = MPI2_SCSIIO_CONTROL_WRITE; + else + io_request->Control = MPI2_SCSIIO_CONTROL_READ; + + io_request->SGLOffset0 = offsetof( + struct mfi_mpi2_request_raid_scsi_io, SGL)/4; + + io_request->SenseBufferLowAddress = mfi_cmd->cm_sense_busaddr; + io_request->SenseBufferLength = MFI_SENSE_LEN; + return 0; +} + +static int +mfi_tbolt_build_cdb(struct mfi_softc *sc, struct mfi_command *mfi_cmd, + uint8_t *cdb) +{ + uint32_t lba_lo, lba_hi, num_lba; + uint8_t cdb_len; + + if (mfi_cmd == NULL || cdb == NULL) + return 1; + num_lba = mfi_cmd->cm_frame->io.header.data_len; + lba_lo = mfi_cmd->cm_frame->io.lba_lo; + lba_hi = mfi_cmd->cm_frame->io.lba_hi; + + if ((num_lba <= 0xFF) && (lba_lo <= 0x1FFFFF)) { + if (mfi_cmd->cm_frame->header.cmd == MFI_CMD_LD_WRITE) + /* Read 6 or Write 6 */ + cdb[0] = (uint8_t) (0x0A); + else + cdb[0] = (uint8_t) (0x08); + + cdb[4] = (uint8_t) num_lba; + cdb[3] = (uint8_t) (lba_lo & 0xFF); + cdb[2] = (uint8_t) (lba_lo >> 8); + cdb[1] = (uint8_t) ((lba_lo >> 16) & 0x1F); + cdb_len = 6; + } + else if ((num_lba <= 0xFFFF) && (lba_lo <= 0xFFFFFFFF)) { + if (mfi_cmd->cm_frame->header.cmd == MFI_CMD_LD_WRITE) + /* Read 10 or Write 10 */ + cdb[0] = (uint8_t) (0x2A); + else + cdb[0] = (uint8_t) (0x28); + cdb[8] = (uint8_t) (num_lba & 0xFF); + cdb[7] = (uint8_t) (num_lba >> 8); + cdb[5] = (uint8_t) (lba_lo & 0xFF); + cdb[4] = (uint8_t) (lba_lo >> 8); + cdb[3] = (uint8_t) (lba_lo >> 16); + cdb[2] = (uint8_t) (lba_lo >> 24); + cdb_len = 10; + } + else if ((num_lba > 0xFFFF) && (lba_hi == 0)) { + if (mfi_cmd->cm_frame->header.cmd == MFI_CMD_LD_WRITE) + /* Read 12 or Write 12 */ + cdb[0] = (uint8_t) (0xAA); + else + cdb[0] = (uint8_t) (0xA8); + cdb[9] = (uint8_t) (num_lba & 0xFF); + cdb[8] = (uint8_t) (num_lba >> 8); + cdb[7] = (uint8_t) (num_lba >> 16); + cdb[6] = (uint8_t) (num_lba >> 24); + cdb[5] = (uint8_t) (lba_lo & 0xFF); + cdb[4] = (uint8_t) (lba_lo >> 8); + cdb[3] = (uint8_t) (lba_lo >> 16); + cdb[2] = (uint8_t) (lba_lo >> 24); + cdb_len = 12; + } else { + if (mfi_cmd->cm_frame->header.cmd == MFI_CMD_LD_WRITE) + cdb[0] = (uint8_t) (0x8A); + else + cdb[0] = (uint8_t) (0x88); + cdb[13] = (uint8_t) (num_lba & 0xFF); + cdb[12] = (uint8_t) (num_lba >> 8); + cdb[11] = (uint8_t) (num_lba >> 16); + cdb[10] = (uint8_t) (num_lba >> 24); + cdb[9] = (uint8_t) (lba_lo & 0xFF); + cdb[8] = (uint8_t) (lba_lo >> 8); + cdb[7] = (uint8_t) (lba_lo >> 16); + cdb[6] = (uint8_t) (lba_lo >> 24); + cdb[5] = (uint8_t) (lba_hi & 0xFF); + cdb[4] = (uint8_t) (lba_hi >> 8); + cdb[3] = (uint8_t) (lba_hi >> 16); + cdb[2] = (uint8_t) (lba_hi >> 24); + cdb_len = 16; + } + return cdb_len; +} + +static int +mfi_tbolt_make_sgl(struct mfi_softc *sc, struct mfi_command *mfi_cmd, + pMpi25IeeeSgeChain64_t sgl_ptr, struct mfi_cmd_tbolt *cmd) +{ + uint8_t i, sg_processed, sg_to_process; + uint8_t sge_count, sge_idx; + union mfi_sgl *os_sgl; + + /* + * Return 0 if there is no data transfer + */ + if (!mfi_cmd->cm_sg || !mfi_cmd->cm_len) { + device_printf(sc->mfi_dev, "Buffer empty \n"); + return 0; + } + os_sgl = mfi_cmd->cm_sg; + sge_count = mfi_cmd->cm_frame->header.sg_count; + + if (sge_count > sc->mfi_max_sge) { + device_printf(sc->mfi_dev, "sgl ptr %p sg_cnt %d \n", + os_sgl, sge_count); + return sge_count; + } + + if (sge_count > sc->max_SGEs_in_main_message) + /* One element to store the chain info */ + sge_idx = sc->max_SGEs_in_main_message - 1; + else + sge_idx = sge_count; + + for (i = 0; i < sge_idx; i++) { + /* + * For 32bit BSD we are getting 32 bit SGL's from OS + * but FW only take 64 bit SGL's so copying from 32 bit + * SGL's to 64. + */ + if (sc->mfi_flags & MFI_FLAGS_SKINNY) { + sgl_ptr->Length = os_sgl->sg_skinny[i].len; + sgl_ptr->Address = os_sgl->sg_skinny[i].addr; + } else { + sgl_ptr->Length = os_sgl->sg32[i].len; + sgl_ptr->Address = os_sgl->sg32[i].addr; + } + sgl_ptr->Flags = 0; + sgl_ptr++; + cmd->io_request->ChainOffset = 0; + } + + sg_processed = i; + + if (sg_processed < sge_count) { + pMpi25IeeeSgeChain64_t sg_chain; + sg_to_process = sge_count - sg_processed; + cmd->io_request->ChainOffset = + sc->chain_offset_value_for_main_message; + sg_chain = sgl_ptr; + /* Prepare chain element */ + sg_chain->NextChainOffset = 0; + sg_chain->Flags = (MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT | + MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR); + sg_chain->Length = (sizeof(MPI2_SGE_IO_UNION) * + (sge_count - sg_processed)); + sg_chain->Address = cmd->sg_frame_phys_addr; + sgl_ptr = (pMpi25IeeeSgeChain64_t)cmd->sg_frame; + for (; i < sge_count; i++) { + if (sc->mfi_flags & MFI_FLAGS_SKINNY) { + sgl_ptr->Length = os_sgl->sg_skinny[i].len; + sgl_ptr->Address = os_sgl->sg_skinny[i].addr; + } else { + sgl_ptr->Length = os_sgl->sg32[i].len; + sgl_ptr->Address = os_sgl->sg32[i].addr; + } + sgl_ptr->Flags = 0; + sgl_ptr++; + } + } + return sge_count; +} + +union mfi_mpi2_request_descriptor * +mfi_build_and_issue_cmd(struct mfi_softc *sc, struct mfi_command *mfi_cmd) +{ + struct mfi_cmd_tbolt *cmd; + union mfi_mpi2_request_descriptor *req_desc = NULL; + uint16_t index; + cmd = mfi_tbolt_get_cmd(sc); + if (!cmd) + return NULL; + mfi_cmd->cm_extra_frames = cmd->index; + cmd->sync_cmd_idx = mfi_cmd->cm_index; + + index = cmd->index; + req_desc = mfi_tbolt_get_request_descriptor(sc, index-1); + if (mfi_tbolt_build_io(sc, mfi_cmd, cmd)) + return NULL; + req_desc->header.SMID = index; + return req_desc; +} + +union mfi_mpi2_request_descriptor * +mfi_tbolt_build_mpt_cmd(struct mfi_softc *sc, struct mfi_command *cmd) +{ + union mfi_mpi2_request_descriptor *req_desc = NULL; + uint16_t index; + if (mfi_build_mpt_pass_thru(sc, cmd)) { + device_printf(sc->mfi_dev, "Couldn't build MFI pass thru " + "cmd\n"); + return NULL; + } + /* For fusion the frame_count variable is used for SMID */ + index = cmd->cm_extra_frames; + + req_desc = mfi_tbolt_get_request_descriptor(sc, index - 1); + if (!req_desc) + return NULL; + + bzero(req_desc, sizeof(req_desc)); + req_desc->header.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO << + MFI_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); + req_desc->header.SMID = index; + return req_desc; +} + +int +mfi_tbolt_send_frame(struct mfi_softc *sc, struct mfi_command *cm) +{ + struct mfi_frame_header *hdr; + uint8_t *cdb; + union mfi_mpi2_request_descriptor *req_desc = NULL; + int tm = MFI_POLL_TIMEOUT_SECS * 1000; + + hdr = &cm->cm_frame->header; + cdb = cm->cm_frame->pass.cdb; + if (sc->adpreset) + return 1; + if ((cm->cm_flags & MFI_CMD_POLLED) == 0) { + cm->cm_timestamp = time_uptime; + mfi_enqueue_busy(cm); + } + else { + hdr->cmd_status = 0xff; + hdr->flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE; + } + + if (hdr->cmd == MFI_CMD_PD_SCSI_IO) { + /* check for inquiry commands coming from CLI */ + if (cdb[0] != 0x28 || cdb[0] != 0x2A) { + if ((req_desc = mfi_tbolt_build_mpt_cmd(sc, cm)) == + NULL) { + device_printf(sc->mfi_dev, "Mapping from MFI " + "to MPT Failed \n"); + return 1; + } + } + else + device_printf(sc->mfi_dev, "DJA NA XXX SYSPDIO\n"); + } + else if (hdr->cmd == MFI_CMD_LD_SCSI_IO || + hdr->cmd == MFI_CMD_LD_READ || hdr->cmd == MFI_CMD_LD_WRITE) { + if ((req_desc = mfi_build_and_issue_cmd(sc, cm)) == NULL) { + device_printf(sc->mfi_dev, "LDIO Failed \n"); + return 1; + } + } else + if ((req_desc = mfi_tbolt_build_mpt_cmd(sc, cm)) == NULL) { + device_printf(sc->mfi_dev, "Mapping from MFI to MPT " + "Failed\n"); + return 1; + } + MFI_WRITE4(sc, MFI_ILQP, (req_desc->words & 0xFFFFFFFF)); + MFI_WRITE4(sc, MFI_IHQP, (req_desc->words >>0x20)); + + if ((cm->cm_flags & MFI_CMD_POLLED) == 0) + return 0; + + /* This is a polled command, so busy-wait for it to complete. */ + while (hdr->cmd_status == 0xff) { + DELAY(1000); + tm -= 1; + if (tm <= 0) + break; + } + + if (hdr->cmd_status == 0xff) { + device_printf(sc->mfi_dev, "Frame %p timed out " + "command 0x%X\n", hdr, cm->cm_frame->dcmd.opcode); + return (ETIMEDOUT); + } + return 0; +} + +static void mfi_issue_pending_cmds_again (struct mfi_softc *sc) +{ + struct mfi_command *cm, *tmp; + + mtx_assert(&sc->mfi_io_lock, MA_OWNED); + TAILQ_FOREACH_REVERSE_SAFE(cm, &sc->mfi_busy, BUSYQ, cm_link, tmp) { + + cm->retry_for_fw_reset++; + + /* + * If a command has continuously been tried multiple times + * and causing a FW reset condition, no further recoveries + * should be performed on the controller + */ + if (cm->retry_for_fw_reset == 3) { + device_printf(sc->mfi_dev, "megaraid_sas: command %d " + "was tried multiple times during adapter reset" + "Shutting down the HBA\n", cm->cm_index); + mfi_kill_hba(sc); + sc->hw_crit_error = 1; + return; + } + + if ((cm->cm_flags & MFI_ON_MFIQ_BUSY) != 0) { + struct mfi_cmd_tbolt *cmd; + mfi_remove_busy(cm); + cmd = sc->mfi_cmd_pool_tbolt[cm->cm_extra_frames - + 1 ]; + mfi_tbolt_return_cmd(sc, cmd); + if ((cm->cm_flags & MFI_ON_MFIQ_MASK) == 0) { + if (cm->cm_frame->dcmd.opcode != + MFI_DCMD_CTRL_EVENT_WAIT) { + device_printf(sc->mfi_dev, + "APJ ****requeue command %d \n", + cm->cm_index); + mfi_requeue_ready(cm); + } + } + else + mfi_release_command(cm); + } + } + mfi_startio(sc); +} + +static void mfi_kill_hba (struct mfi_softc *sc) +{ + if (sc->mfi_flags & MFI_FLAGS_TBOLT) + MFI_WRITE4 (sc, 0x00,MFI_STOP_ADP); + else + MFI_WRITE4 (sc, MFI_IDB,MFI_STOP_ADP); +} + +static void mfi_process_fw_state_chg_isr(void *arg) +{ + struct mfi_softc *sc= (struct mfi_softc *)arg; + struct mfi_cmd_tbolt *cmd; + int error, status; + + if (sc->adpreset == 1) { + device_printf(sc->mfi_dev, "First stage of FW reset " + "initiated...\n"); + + sc->mfi_adp_reset(sc); + sc->mfi_enable_intr(sc); + + device_printf(sc->mfi_dev, "First stage of reset complete, " + "second stage initiated...\n"); + + sc->adpreset = 2; + + /* waiting for about 20 second before start the second init */ + for (int wait = 0; wait < 20000; wait++) + DELAY(1000); + device_printf(sc->mfi_dev, "Second stage of FW reset " + "initiated...\n"); + while ((status = MFI_READ4(sc, MFI_RSR)) & 0x04); + + sc->mfi_disable_intr(sc); + + /* We expect the FW state to be READY */ + if (mfi_transition_firmware(sc)) { + device_printf(sc->mfi_dev, "controller is not in " + "ready state\n"); + mfi_kill_hba(sc); + sc->hw_crit_error= 1; + return ; + } + if ((error = mfi_tbolt_init_MFI_queue(sc)) != 0) + return; + + mtx_lock(&sc->mfi_io_lock); + + sc->mfi_enable_intr(sc); + sc->adpreset = 0; + free(sc->mfi_aen_cm->cm_data, M_MFIBUF); + mfi_remove_busy(sc->mfi_aen_cm); + cmd = sc->mfi_cmd_pool_tbolt[sc->mfi_aen_cm->cm_extra_frames + - 1]; + mfi_tbolt_return_cmd(sc, cmd); + if (sc->mfi_aen_cm) { + mfi_release_command(sc->mfi_aen_cm); + sc->mfi_aen_cm = NULL; + } + if (sc->map_update_cmd) { + mfi_release_command(sc->map_update_cmd); + sc->map_update_cmd = NULL; + } + mfi_issue_pending_cmds_again(sc); + + /* + * Issue pending command can result in adapter being marked + * dead because of too many re-tries. Check for that + * condition before clearing the reset condition on the FW + */ + if (!sc->hw_crit_error) { + /* + * Initiate AEN (Asynchronous Event Notification) + */ + mfi_aen_setup(sc, sc->last_seq_num); + sc->issuepend_done = 1; + device_printf(sc->mfi_dev, "second stage of reset " + "complete, FW is ready now.\n"); + } else { + device_printf(sc->mfi_dev, "second stage of reset " + "never completed, hba was marked offline.\n"); + } + } else { + device_printf(sc->mfi_dev, "mfi_process_fw_state_chg_isr " + "called with unhandled value:%d\n", sc->adpreset); + } + mtx_unlock(&sc->mfi_io_lock); +} diff --git a/sys/dev/mfi/mfireg.h b/sys/dev/mfi/mfireg.h index f005c37..627b759 100644 --- a/sys/dev/mfi/mfireg.h +++ b/sys/dev/mfi/mfireg.h @@ -64,7 +64,7 @@ __FBSDID("$FreeBSD$"); * reason why this interface should be limited to just SAS. In any case, LSI * seems to also call this interface 'MFI', so that will be used here. */ - +#define MEGAMFI_FRAME_SIZE 64 /* * Start with the register set. All registers are 32 bits wide. * The usual Intel IOP style setup. @@ -83,25 +83,50 @@ __FBSDID("$FreeBSD$"); #define MFI_OQP 0x44 /* Outbound queue port */ /* +* ThunderBolt specific Register +*/ + +#define MFI_RPI 0x6c /* reply_post_host_index */ +#define MFI_ILQP 0xc0 /* inbound_low_queue_port */ +#define MFI_IHQP 0xc4 /* inbound_high_queue_port */ + +/* * 1078 specific related register */ #define MFI_ODR0 0x9c /* outbound doorbell register0 */ #define MFI_ODCR0 0xa0 /* outbound doorbell clear register0 */ #define MFI_OSP0 0xb0 /* outbound scratch pad0 */ #define MFI_1078_EIM 0x80000004 /* 1078 enable intrrupt mask */ -#define MFI_RMI 0x2 /* reply message interrupt */ +#define MFI_RMI 0x2 /* reply message interrupt */ #define MFI_1078_RM 0x80000000 /* reply 1078 message interrupt */ #define MFI_ODC 0x4 /* outbound doorbell change interrupt */ +/* OCR registers */ +#define MFI_WSR 0x004 /* write sequence register */ +#define MFI_HDR 0x008 /* host diagnostic register */ +#define MFI_RSR 0x3c3 /* Reset Status Register */ + /* * GEN2 specific changes */ #define MFI_GEN2_EIM 0x00000005 /* GEN2 enable interrupt mask */ #define MFI_GEN2_RM 0x00000001 /* reply GEN2 message interrupt */ +/* + * skinny specific changes + */ +#define MFI_SKINNY_IDB 0x00 /* Inbound doorbell is at 0x00 for skinny */ +#define MFI_IQPL 0x000000c0 +#define MFI_IQPH 0x000000c4 +#define MFI_SKINNY_RM 0x00000001 /* reply skinny message interrupt */ + /* Bits for MFI_OSTS */ #define MFI_OSTS_INTR_VALID 0x00000002 +/* OCR specific flags */ +#define MFI_FIRMWARE_STATE_CHANGE 0x00000002 +#define MFI_STATE_CHANGE_INTERRUPT 0x00000004 /* MFI state change interrrupt */ + /* * Firmware state values. Found in OMSG0 during initialization. */ @@ -119,6 +144,16 @@ __FBSDID("$FreeBSD$"); #define MFI_FWSTATE_FAULT 0xf0000000 #define MFI_FWSTATE_MAXSGL_MASK 0x00ff0000 #define MFI_FWSTATE_MAXCMD_MASK 0x0000ffff +#define MFI_FWSTATE_HOSTMEMREQD_MASK 0x08000000 +#define MFI_FWSTATE_BOOT_MESSAGE_PENDING 0x90000000 +#define MFI_RESET_REQUIRED 0x00000001 + +/* ThunderBolt Support */ +#define MFI_FWSTATE_TB_MASK 0xf0000000 +#define MFI_FWSTATE_TB_RESET 0x00000000 +#define MFI_FWSTATE_TB_READY 0x10000000 +#define MFI_FWSTATE_TB_OPERATIONAL 0x20000000 +#define MFI_FWSTATE_TB_FAULT 0x40000000 /* * Control bits to drive the card to ready state. These go into the IDB @@ -130,6 +165,12 @@ __FBSDID("$FreeBSD$"); #define MFI_FWINIT_CLEAR_HANDSHAKE 0x00000008 /* Respond to WAIT_HANDSHAKE */ #define MFI_FWINIT_HOTPLUG 0x00000010 +/* ADP reset flags */ +#define MFI_STOP_ADP 0x00000020 +#define MFI_ADP_RESET 0x00000040 +#define DIAG_WRITE_ENABLE 0x00000080 +#define DIAG_RESET_ADAPTER 0x00000004 + /* MFI Commands */ typedef enum { MFI_CMD_INIT = 0x00, @@ -146,6 +187,7 @@ typedef enum { /* Direct commands */ typedef enum { MFI_DCMD_CTRL_GETINFO = 0x01010000, + MFI_DCMD_CTRL_MFI_HOST_MEM_ALLOC =0x0100e100, MFI_DCMD_CTRL_MFC_DEFAULTS_GET =0x010e0201, MFI_DCMD_CTRL_MFC_DEFAULTS_SET =0x010e0202, MFI_DCMD_CTRL_FLUSHCACHE = 0x01101000, @@ -164,6 +206,7 @@ typedef enum { MFI_DCMD_FLASH_FW_FLASH = 0x010f0300, MFI_DCMD_FLASH_FW_CLOSE = 0x010f0400, MFI_DCMD_PD_GET_LIST = 0x02010000, + MFI_DCMD_PD_LIST_QUERY = 0x02010100, MFI_DCMD_PD_GET_INFO = 0x02020000, MFI_DCMD_PD_STATE_SET = 0x02030100, MFI_DCMD_PD_REBUILD_START = 0x02040100, @@ -173,6 +216,8 @@ typedef enum { MFI_DCMD_PD_GET_PROGRESS = 0x02060000, MFI_DCMD_PD_LOCATE_START = 0x02070100, MFI_DCMD_PD_LOCATE_STOP = 0x02070200, + MFI_DCMD_LD_MAP_GET_INFO = 0x0300e101, + MFI_DCMD_LD_SYNC = 0x0300e102, MFI_DCMD_LD_GET_LIST = 0x03010000, MFI_DCMD_LD_GET_INFO = 0x03020000, MFI_DCMD_LD_GET_PROP = 0x03030000, @@ -183,7 +228,7 @@ typedef enum { MFI_DCMD_CFG_ADD = 0x04020000, MFI_DCMD_CFG_CLEAR = 0x04030000, MFI_DCMD_CFG_MAKE_SPARE = 0x04040000, - MFI_DCMD_CFG_REMOVE_SPARE = 0x04050000, + MFI_DCMD_CFG_REMOVE_SPARE = 0x04050000, MFI_DCMD_CFG_FOREIGN_IMPORT = 0x04060400, MFI_DCMD_BBU_GET_STATUS = 0x05010000, MFI_DCMD_BBU_GET_CAPACITY_INFO =0x05020000, @@ -213,6 +258,36 @@ typedef enum { #define MFI_FRAME_DIR_WRITE 0x0008 #define MFI_FRAME_DIR_READ 0x0010 #define MFI_FRAME_DIR_BOTH 0x0018 +#define MFI_FRAME_IEEE_SGL 0x0020 + +/* ThunderBolt Specific */ + +/* + * Pre-TB command size and TB command size. + * We will be checking it at the load time for the time being + */ +#define MR_COMMAND_SIZE (MFI_FRAME_SIZE*20) /* 1280 bytes */ + +#define MEGASAS_THUNDERBOLT_MSG_ALLIGNMENT 256 +/* + * We are defining only 128 byte message to reduce memory move over head + * and also it will reduce the SRB extension size by 128byte compared with + * 256 message size + */ +#define MEGASAS_THUNDERBOLT_NEW_MSG_SIZE 256 +#define MEGASAS_THUNDERBOLT_MAX_COMMANDS 1024 +#define MEGASAS_THUNDERBOLT_MAX_REPLY_COUNT 1024 +#define MEGASAS_THUNDERBOLT_REPLY_SIZE 8 +#define MEGASAS_THUNDERBOLT_MAX_CHAIN_COUNT 1 +#define MEGASAS_MAX_SZ_CHAIN_FRAME 1024 + +#define MPI2_FUNCTION_PASSTHRU_IO_REQUEST 0xF0 +#define MPI2_FUNCTION_LD_IO_REQUEST 0xF1 + +#define MR_INTERNAL_MFI_FRAMES_SMID 1 +#define MR_CTRL_EVENT_WAIT_SMID 2 +#define MR_INTERNAL_DRIVER_RESET_SMID 3 + /* MFI Status codes */ typedef enum { @@ -325,6 +400,10 @@ typedef enum { MR_EVT_ARGS_ECC } mfi_evt_args; +#define MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED 0x0152 +#define MR_EVT_PD_REMOVED 0x0070 +#define MR_EVT_PD_INSERTED 0x005b + typedef enum { MR_LD_CACHE_WRITE_BACK = 0x01, MR_LD_CACHE_WRITE_ADAPTIVE = 0x02, @@ -352,6 +431,15 @@ typedef enum { MR_PD_CACHE_DISABLE = 2 } mfi_pd_cache; +typedef enum { + MR_PD_QUERY_TYPE_ALL = 0, + MR_PD_QUERY_TYPE_STATE = 1, + MR_PD_QUERY_TYPE_POWER_STATE = 2, + MR_PD_QUERY_TYPE_MEDIA_TYPE = 3, + MR_PD_QUERY_TYPE_SPEED = 4, + MR_PD_QUERY_TYPE_EXPOSED_TO_HOST = 5 /*query for system drives */ +} mfi_pd_query_type; + /* * Other propertities and definitions */ @@ -384,9 +472,16 @@ struct mfi_sg64 { uint32_t len; } __packed; +struct mfi_sg_skinny { + uint64_t addr; + uint32_t len; + uint32_t flag; +} __packed; + union mfi_sgl { - struct mfi_sg32 sg32[1]; - struct mfi_sg64 sg64[1]; + struct mfi_sg32 sg32[1]; + struct mfi_sg64 sg64[1]; + struct mfi_sg_skinny sg_skinny[1]; } __packed; /* Message frames. All messages have a common header */ @@ -400,6 +495,10 @@ struct mfi_frame_header { uint8_t cdb_len; uint8_t sg_count; uint32_t context; + /* + * pad0 is MSI Specific. Not used by Driver. Zero the value before + * sending the command to f/w. + */ uint32_t pad0; uint16_t flags; #define MFI_FRAME_DATAOUT 0x08 @@ -414,9 +513,29 @@ struct mfi_init_frame { uint32_t qinfo_new_addr_hi; uint32_t qinfo_old_addr_lo; uint32_t qinfo_old_addr_hi; - uint32_t reserved[6]; + // Start LSIP200113393 + uint32_t driver_ver_lo; /*28h */ + uint32_t driver_ver_hi; /*2Ch */ + + uint32_t reserved[4]; + // End LSIP200113393 } __packed; +/* + * Define MFI Address Context union. + */ +#ifdef MFI_ADDRESS_IS_uint64_t + typedef uint64_t MFI_ADDRESS; +#else + typedef union _MFI_ADDRESS { + struct { + uint32_t addressLow; + uint32_t addressHigh; + } u; + uint64_t address; + } MFI_ADDRESS, *PMFI_ADDRESS; +#endif + #define MFI_IO_FRAME_SIZE 40 struct mfi_io_frame { struct mfi_frame_header header; @@ -447,10 +566,11 @@ struct mfi_dcmd_frame { struct mfi_abort_frame { struct mfi_frame_header header; uint32_t abort_context; - uint32_t pad; + /* pad is changed to reserved.*/ + uint32_t reserved0; uint32_t abort_mfi_addr_lo; uint32_t abort_mfi_addr_hi; - uint32_t reserved[6]; + uint32_t reserved1[6]; } __packed; struct mfi_smp_frame { @@ -475,6 +595,7 @@ struct mfi_stp_frame { union mfi_frame { struct mfi_frame_header header; struct mfi_init_frame init; + /* ThunderBolt Initialization */ struct mfi_io_frame io; struct mfi_pass_frame pass; struct mfi_dcmd_frame dcmd; @@ -524,7 +645,61 @@ struct mfi_ctrl_props { uint16_t ecc_bucket_leak_rate; uint8_t restore_hotspare_on_insertion; uint8_t expose_encl_devices; - uint8_t reserved[38]; + uint8_t maintainPdFailHistory; + uint8_t disallowHostRequestReordering; + /* set TRUE to abort CC on detecting an inconsistency */ + uint8_t abortCCOnError; + /* load balance mode (MR_LOAD_BALANCE_MODE) */ + uint8_t loadBalanceMode; + /* + * 0 - use auto detect logic of backplanes like SGPIO, i2c SEP using + * h/w mechansim like GPIO pins + * 1 - disable auto detect SGPIO, + * 2 - disable i2c SEP auto detect + * 3 - disable both auto detect + */ + uint8_t disableAutoDetectBackplane; + /* + * % of source LD to be reserved for a VDs snapshot in snapshot + * repository, for metadata and user data: 1=5%, 2=10%, 3=15% and so on + */ + uint8_t snapVDSpace; + + /* + * Add properties that can be controlled by a bit in the following + * structure. + */ + struct { + /* set TRUE to disable copyBack (0=copback enabled) */ + uint32_t copyBackDisabled :1; + uint32_t SMARTerEnabled :1; + uint32_t prCorrectUnconfiguredAreas :1; + uint32_t useFdeOnly :1; + uint32_t disableNCQ :1; + uint32_t SSDSMARTerEnabled :1; + uint32_t SSDPatrolReadEnabled :1; + uint32_t enableSpinDownUnconfigured :1; + uint32_t autoEnhancedImport :1; + uint32_t enableSecretKeyControl :1; + uint32_t disableOnlineCtrlReset :1; + uint32_t allowBootWithPinnedCache :1; + uint32_t disableSpinDownHS :1; + uint32_t enableJBOD :1; + uint32_t reserved :18; + } OnOffProperties; + /* + * % of source LD to be reserved for auto snapshot in snapshot + * repository, for metadata and user data: 1=5%, 2=10%, 3=15% and so on. + */ + uint8_t autoSnapVDSpace; + /* + * Snapshot writeable VIEWs capacity as a % of source LD capacity: + * 0=READ only, 1=5%, 2=10%, 3=15% and so on. + */ + uint8_t viewSpace; + /* # of idle minutes before device is spun down (0=use FW defaults) */ + uint16_t spinDownTime; + uint8_t reserved[24]; } __packed; /* PCI information about the card. */ @@ -964,10 +1139,11 @@ struct mfi_pd_address { uint64_t sas_addr[2]; } __packed; +#define MAX_SYS_PDS 240 struct mfi_pd_list { uint32_t size; uint32_t count; - struct mfi_pd_address addr[0]; + struct mfi_pd_address addr[MAX_SYS_PDS]; } __packed; enum mfi_pd_state { @@ -982,6 +1158,12 @@ enum mfi_pd_state { MFI_PD_STATE_SYSTEM = 0x40 }; +/* + * "SYSTEM" disk appears to be "JBOD" support from the RAID controller. + * Adding a #define to denote this. + */ +#define MFI_PD_STATE_JBOD MFI_PD_STATE_SYSTEM + union mfi_ld_ref { struct { uint8_t target_id; @@ -1040,7 +1222,9 @@ struct mfi_ld_params { #define MFI_LD_PARAMS_INIT_QUICK 1 #define MFI_LD_PARAMS_INIT_FULL 2 uint8_t is_consistent; - uint8_t reserved[23]; + uint8_t reserved1[6]; + uint8_t isSSCD; + uint8_t reserved2[16]; } __packed; struct mfi_ld_progress { @@ -1081,7 +1265,7 @@ struct mfi_ld_info { uint8_t reserved2[16]; } __packed; -#define MAX_ARRAYS 16 +#define MFI_MAX_ARRAYS 16 struct mfi_spare { union mfi_pd_ref ref; uint8_t spare_type; @@ -1090,9 +1274,10 @@ struct mfi_spare { #define MFI_SPARE_ENCL_AFFINITY (1 << 2) uint8_t reserved[2]; uint8_t array_count; - uint16_t array_ref[MAX_ARRAYS]; + uint16_t array_ref[MFI_MAX_ARRAYS]; } __packed; +#define MFI_MAX_ROW_SIZE 32 struct mfi_array { uint64_t size; uint8_t num_drives; @@ -1106,7 +1291,7 @@ struct mfi_array { uint8_t pd; uint8_t slot; } encl; - } pd[0]; + } pd[MFI_MAX_ROW_SIZE]; } __packed; struct mfi_config_data { @@ -1182,7 +1367,7 @@ struct mfi_bbu_status { uint8_t battery_type; #define MFI_BBU_TYPE_NONE 0 #define MFI_BBU_TYPE_IBBU 1 -#define MFI_BBU_TYPE_BBU 2 +#define MFI_BBU_TYPE_BBU 2 uint8_t reserved; uint16_t voltage; int16_t current; @@ -1230,6 +1415,450 @@ struct mfi_pr_properties { uint32_t clear_freq; }; +/* ThunderBolt support */ + +/* + * Raid Context structure which describes MegaRAID specific IO Paramenters + * This resides at offset 0x60 where the SGL normally starts in MPT IO Frames + */ +typedef struct _MPI2_SCSI_IO_VENDOR_UNIQUE { + uint16_t resvd0; /* 0x00 - 0x01 */ + uint16_t timeoutValue; /* 0x02 - 0x03 */ + uint8_t regLockFlags; + uint8_t armId; + uint16_t TargetID; /* 0x06 - 0x07 */ + + uint64_t RegLockLBA; /* 0x08 - 0x0F */ + + uint32_t RegLockLength; /* 0x10 - 0x13 */ + + uint16_t SMID; /* 0x14 - 0x15 nextLMId */ + uint8_t exStatus; /* 0x16 */ + uint8_t Status; /* 0x17 status */ + + uint8_t RAIDFlags; /* 0x18 */ + uint8_t numSGE; /* 0x19 numSge */ + uint16_t configSeqNum; /* 0x1A - 0x1B */ + uint8_t spanArm; /* 0x1C */ + uint8_t resvd2[3]; /* 0x1D - 0x1F */ +} MPI2_SCSI_IO_VENDOR_UNIQUE, MPI25_SCSI_IO_VENDOR_UNIQUE; + +/***************************************************************************** +* +* Message Functions +* +*****************************************************************************/ + +#define NA_MPI2_FUNCTION_SCSI_IO_REQUEST (0x00) /* SCSI IO */ +#define MPI2_FUNCTION_SCSI_TASK_MGMT (0x01) /* SCSI Task Management */ +#define MPI2_FUNCTION_IOC_INIT (0x02) /* IOC Init */ +#define MPI2_FUNCTION_IOC_FACTS (0x03) /* IOC Facts */ +#define MPI2_FUNCTION_CONFIG (0x04) /* Configuration */ +#define MPI2_FUNCTION_PORT_FACTS (0x05) /* Port Facts */ +#define MPI2_FUNCTION_PORT_ENABLE (0x06) /* Port Enable */ +#define MPI2_FUNCTION_EVENT_NOTIFICATION (0x07) /* Event Notification */ +#define MPI2_FUNCTION_EVENT_ACK (0x08) /* Event Acknowledge */ +#define MPI2_FUNCTION_FW_DOWNLOAD (0x09) /* FW Download */ +#define MPI2_FUNCTION_TARGET_ASSIST (0x0B) /* Target Assist */ +#define MPI2_FUNCTION_TARGET_STATUS_SEND (0x0C) /* Target Status Send */ +#define MPI2_FUNCTION_TARGET_MODE_ABORT (0x0D) /* Target Mode Abort */ +#define MPI2_FUNCTION_FW_UPLOAD (0x12) /* FW Upload */ +#define MPI2_FUNCTION_RAID_ACTION (0x15) /* RAID Action */ +#define MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH (0x16) /* SCSI IO RAID Passthrough */ +#define MPI2_FUNCTION_TOOLBOX (0x17) /* Toolbox */ +#define MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR (0x18) /* SCSI Enclosure Processor */ +#define MPI2_FUNCTION_SMP_PASSTHROUGH (0x1A) /* SMP Passthrough */ +#define MPI2_FUNCTION_SAS_IO_UNIT_CONTROL (0x1B) /* SAS IO Unit Control */ +#define MPI2_FUNCTION_SATA_PASSTHROUGH (0x1C) /* SATA Passthrough */ +#define MPI2_FUNCTION_DIAG_BUFFER_POST (0x1D) /* Diagnostic Buffer Post */ +#define MPI2_FUNCTION_DIAG_RELEASE (0x1E) /* Diagnostic Release */ +#define MPI2_FUNCTION_TARGET_CMD_BUF_BASE_POST (0x24) /* Target Command Buffer Post Base */ +#define MPI2_FUNCTION_TARGET_CMD_BUF_LIST_POST (0x25) /* Target Command Buffer Post List */ +#define MPI2_FUNCTION_RAID_ACCELERATOR (0x2C) /* RAID Accelerator */ +#define MPI2_FUNCTION_HOST_BASED_DISCOVERY_ACTION (0x2F) /* Host Based Discovery Action */ +#define MPI2_FUNCTION_PWR_MGMT_CONTROL (0x30) /* Power Management Control */ +#define MPI2_FUNCTION_MIN_PRODUCT_SPECIFIC (0xF0) /* beginning of product-specific range */ +#define MPI2_FUNCTION_MAX_PRODUCT_SPECIFIC (0xFF) /* end of product-specific range */ + +/* Doorbell functions */ +#define MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET (0x40) +#define MPI2_FUNCTION_HANDSHAKE (0x42) + +/***************************************************************************** +* +* MPI Version Definitions +* +*****************************************************************************/ + +#define MPI2_VERSION_MAJOR (0x02) +#define MPI2_VERSION_MINOR (0x00) +#define MPI2_VERSION_MAJOR_MASK (0xFF00) +#define MPI2_VERSION_MAJOR_SHIFT (8) +#define MPI2_VERSION_MINOR_MASK (0x00FF) +#define MPI2_VERSION_MINOR_SHIFT (0) +#define MPI2_VERSION ((MPI2_VERSION_MAJOR << MPI2_VERSION_MAJOR_SHIFT) | \ + MPI2_VERSION_MINOR) + +#define MPI2_VERSION_02_00 (0x0200) + +/* versioning for this MPI header set */ +#define MPI2_HEADER_VERSION_UNIT (0x10) +#define MPI2_HEADER_VERSION_DEV (0x00) +#define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00) +#define MPI2_HEADER_VERSION_UNIT_SHIFT (8) +#define MPI2_HEADER_VERSION_DEV_MASK (0x00FF) +#define MPI2_HEADER_VERSION_DEV_SHIFT (0) +#define MPI2_HEADER_VERSION ((MPI2_HEADER_VERSION_UNIT << 8) | \ + MPI2_HEADER_VERSION_DEV) + + +/* IOCInit Request message */ +struct MPI2_IOC_INIT_REQUEST { + uint8_t WhoInit; /* 0x00 */ + uint8_t Reserved1; /* 0x01 */ + uint8_t ChainOffset; /* 0x02 */ + uint8_t Function; /* 0x03 */ + uint16_t Reserved2; /* 0x04 */ + uint8_t Reserved3; /* 0x06 */ + uint8_t MsgFlags; /* 0x07 */ + uint8_t VP_ID; /* 0x08 */ + uint8_t VF_ID; /* 0x09 */ + uint16_t Reserved4; /* 0x0A */ + uint16_t MsgVersion; /* 0x0C */ + uint16_t HeaderVersion; /* 0x0E */ + uint32_t Reserved5; /* 0x10 */ + uint16_t Reserved6; /* 0x14 */ + uint8_t Reserved7; /* 0x16 */ + uint8_t HostMSIxVectors; /* 0x17 */ + uint16_t Reserved8; /* 0x18 */ + uint16_t SystemRequestFrameSize; /* 0x1A */ + uint16_t ReplyDescriptorPostQueueDepth; /* 0x1C */ + uint16_t ReplyFreeQueueDepth; /* 0x1E */ + uint32_t SenseBufferAddressHigh; /* 0x20 */ + uint32_t SystemReplyAddressHigh; /* 0x24 */ + uint64_t SystemRequestFrameBaseAddress; /* 0x28 */ + uint64_t ReplyDescriptorPostQueueAddress;/* 0x30 */ + uint64_t ReplyFreeQueueAddress; /* 0x38 */ + uint64_t TimeStamp; /* 0x40 */ +}; + +/* WhoInit values */ +#define MPI2_WHOINIT_NOT_INITIALIZED (0x00) +#define MPI2_WHOINIT_SYSTEM_BIOS (0x01) +#define MPI2_WHOINIT_ROM_BIOS (0x02) +#define MPI2_WHOINIT_PCI_PEER (0x03) +#define MPI2_WHOINIT_HOST_DRIVER (0x04) +#define MPI2_WHOINIT_MANUFACTURER (0x05) + +struct MPI2_SGE_CHAIN_UNION { + uint16_t Length; + uint8_t NextChainOffset; + uint8_t Flags; + union { + uint32_t Address32; + uint64_t Address64; + } u; +}; + +struct MPI2_IEEE_SGE_SIMPLE32 { + uint32_t Address; + uint32_t FlagsLength; +}; + +struct MPI2_IEEE_SGE_SIMPLE64 { + uint64_t Address; + uint32_t Length; + uint16_t Reserved1; + uint8_t Reserved2; + uint8_t Flags; +}; + +typedef union _MPI2_IEEE_SGE_SIMPLE_UNION { + struct MPI2_IEEE_SGE_SIMPLE32 Simple32; + struct MPI2_IEEE_SGE_SIMPLE64 Simple64; +} MPI2_IEEE_SGE_SIMPLE_UNION; + +typedef struct _MPI2_SGE_SIMPLE_UNION { + uint32_t FlagsLength; + union { + uint32_t Address32; + uint64_t Address64; + } u; +} MPI2_SGE_SIMPLE_UNION; + +/**************************************************************************** +* IEEE SGE field definitions and masks +****************************************************************************/ + +/* Flags field bit definitions */ + +#define MPI2_IEEE_SGE_FLAGS_ELEMENT_TYPE_MASK (0x80) + +#define MPI2_IEEE32_SGE_FLAGS_SHIFT (24) + +#define MPI2_IEEE32_SGE_LENGTH_MASK (0x00FFFFFF) + +/* Element Type */ + +#define MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT (0x00) +#define MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT (0x80) + +/* Data Location Address Space */ + +#define MPI2_IEEE_SGE_FLAGS_ADDR_MASK (0x03) +#define MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR (0x00) +#define MPI2_IEEE_SGE_FLAGS_IOCDDR_ADDR (0x01) +#define MPI2_IEEE_SGE_FLAGS_IOCPLB_ADDR (0x02) +#define MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR (0x03) + +/* Address Size */ + +#define MPI2_SGE_FLAGS_32_BIT_ADDRESSING (0x00) +#define MPI2_SGE_FLAGS_64_BIT_ADDRESSING (0x02) + +/*******************/ +/* SCSI IO Control bits */ +#define MPI2_SCSIIO_CONTROL_ADDCDBLEN_MASK (0xFC000000) +#define MPI2_SCSIIO_CONTROL_ADDCDBLEN_SHIFT (26) + +#define MPI2_SCSIIO_CONTROL_DATADIRECTION_MASK (0x03000000) +#define MPI2_SCSIIO_CONTROL_NODATATRANSFER (0x00000000) +#define MPI2_SCSIIO_CONTROL_WRITE (0x01000000) +#define MPI2_SCSIIO_CONTROL_READ (0x02000000) +#define MPI2_SCSIIO_CONTROL_BIDIRECTIONAL (0x03000000) + +#define MPI2_SCSIIO_CONTROL_TASKPRI_MASK (0x00007800) +#define MPI2_SCSIIO_CONTROL_TASKPRI_SHIFT (11) + +#define MPI2_SCSIIO_CONTROL_TASKATTRIBUTE_MASK (0x00000700) +#define MPI2_SCSIIO_CONTROL_SIMPLEQ (0x00000000) +#define MPI2_SCSIIO_CONTROL_HEADOFQ (0x00000100) +#define MPI2_SCSIIO_CONTROL_ORDEREDQ (0x00000200) +#define MPI2_SCSIIO_CONTROL_ACAQ (0x00000400) + +#define MPI2_SCSIIO_CONTROL_TLR_MASK (0x000000C0) +#define MPI2_SCSIIO_CONTROL_NO_TLR (0x00000000) +#define MPI2_SCSIIO_CONTROL_TLR_ON (0x00000040) +#define MPI2_SCSIIO_CONTROL_TLR_OFF (0x00000080) + +/*******************/ + +typedef struct { + uint8_t CDB[20]; /* 0x00 */ + uint32_t PrimaryReferenceTag; /* 0x14 */ + uint16_t PrimaryApplicationTag; /* 0x18 */ + uint16_t PrimaryApplicationTagMask; /* 0x1A */ + uint32_t TransferLength; /* 0x1C */ +} MPI2_SCSI_IO_CDB_EEDP32; + + +typedef union _MPI2_IEEE_SGE_CHAIN_UNION { + struct MPI2_IEEE_SGE_SIMPLE32 Chain32; + struct MPI2_IEEE_SGE_SIMPLE64 Chain64; +} MPI2_IEEE_SGE_CHAIN_UNION; + +typedef union _MPI2_SIMPLE_SGE_UNION { + MPI2_SGE_SIMPLE_UNION MpiSimple; + MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple; +} MPI2_SIMPLE_SGE_UNION; + +typedef union _MPI2_SGE_IO_UNION { + MPI2_SGE_SIMPLE_UNION MpiSimple; + struct MPI2_SGE_CHAIN_UNION MpiChain; + MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple; + MPI2_IEEE_SGE_CHAIN_UNION IeeeChain; +} MPI2_SGE_IO_UNION; + +typedef union { + uint8_t CDB32[32]; + MPI2_SCSI_IO_CDB_EEDP32 EEDP32; + MPI2_SGE_SIMPLE_UNION SGE; +} MPI2_SCSI_IO_CDB_UNION; + + +/* MPI 2.5 SGLs */ + +#define MPI25_IEEE_SGE_FLAGS_END_OF_LIST (0x40) + +typedef struct _MPI25_IEEE_SGE_CHAIN64 { + uint64_t Address; + uint32_t Length; + uint16_t Reserved1; + uint8_t NextChainOffset; + uint8_t Flags; +} MPI25_IEEE_SGE_CHAIN64, *pMpi25IeeeSgeChain64_t; + +/* use MPI2_IEEE_SGE_FLAGS_ defines for the Flags field */ + + +/********/ + +/* + * RAID SCSI IO Request Message + * Total SGE count will be one less than _MPI2_SCSI_IO_REQUEST + */ +struct mfi_mpi2_request_raid_scsi_io { + uint16_t DevHandle; /* 0x00 */ + uint8_t ChainOffset; /* 0x02 */ + uint8_t Function; /* 0x03 */ + uint16_t Reserved1; /* 0x04 */ + uint8_t Reserved2; /* 0x06 */ + uint8_t MsgFlags; /* 0x07 */ + uint8_t VP_ID; /* 0x08 */ + uint8_t VF_ID; /* 0x09 */ + uint16_t Reserved3; /* 0x0A */ + uint32_t SenseBufferLowAddress; /* 0x0C */ + uint16_t SGLFlags; /* 0x10 */ + uint8_t SenseBufferLength; /* 0x12 */ + uint8_t Reserved4; /* 0x13 */ + uint8_t SGLOffset0; /* 0x14 */ + uint8_t SGLOffset1; /* 0x15 */ + uint8_t SGLOffset2; /* 0x16 */ + uint8_t SGLOffset3; /* 0x17 */ + uint32_t SkipCount; /* 0x18 */ + uint32_t DataLength; /* 0x1C */ + uint32_t BidirectionalDataLength; /* 0x20 */ + uint16_t IoFlags; /* 0x24 */ + uint16_t EEDPFlags; /* 0x26 */ + uint32_t EEDPBlockSize; /* 0x28 */ + uint32_t SecondaryReferenceTag; /* 0x2C */ + uint16_t SecondaryApplicationTag; /* 0x30 */ + uint16_t ApplicationTagTranslationMask; /* 0x32 */ + uint8_t LUN[8]; /* 0x34 */ + uint32_t Control; /* 0x3C */ + MPI2_SCSI_IO_CDB_UNION CDB; /* 0x40 */ + MPI2_SCSI_IO_VENDOR_UNIQUE RaidContext; /* 0x60 */ + MPI2_SGE_IO_UNION SGL; /* 0x80 */ +} __packed; + +/* + * MPT RAID MFA IO Descriptor. + */ +typedef struct _MFI_RAID_MFA_IO_DESCRIPTOR { + uint32_t RequestFlags : 8; + uint32_t MessageAddress1 : 24; /* bits 31:8*/ + uint32_t MessageAddress2; /* bits 61:32 */ +} MFI_RAID_MFA_IO_REQUEST_DESCRIPTOR,*PMFI_RAID_MFA_IO_REQUEST_DESCRIPTOR; + +struct mfi_mpi2_request_header { + uint8_t RequestFlags; /* 0x00 */ + uint8_t MSIxIndex; /* 0x01 */ + uint16_t SMID; /* 0x02 */ + uint16_t LMID; /* 0x04 */ +}; + +/* defines for the RequestFlags field */ +#define MPI2_REQ_DESCRIPT_FLAGS_TYPE_MASK (0x0E) +#define MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO (0x00) +#define MPI2_REQ_DESCRIPT_FLAGS_SCSI_TARGET (0x02) +#define MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY (0x06) +#define MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE (0x08) +#define MPI2_REQ_DESCRIPT_FLAGS_RAID_ACCELERATOR (0x0A) + +#define MPI2_REQ_DESCRIPT_FLAGS_IOC_FIFO_MARKER (0x01) + +struct mfi_mpi2_request_high_priority { + struct mfi_mpi2_request_header header; + uint16_t reserved; +}; + +struct mfi_mpi2_request_scsi_io { + struct mfi_mpi2_request_header header; + uint16_t scsi_io_dev_handle; +}; + +struct mfi_mpi2_request_scsi_target { + struct mfi_mpi2_request_header header; + uint16_t scsi_target_io_index; +}; + +/* Request Descriptors */ +union mfi_mpi2_request_descriptor { + struct mfi_mpi2_request_header header; + struct mfi_mpi2_request_high_priority high_priority; + struct mfi_mpi2_request_scsi_io scsi_io; + struct mfi_mpi2_request_scsi_target scsi_target; + uint64_t words; +}; + + +struct mfi_mpi2_reply_header { + uint8_t ReplyFlags; /* 0x00 */ + uint8_t MSIxIndex; /* 0x01 */ + uint16_t SMID; /* 0x02 */ +}; + +/* defines for the ReplyFlags field */ +#define MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK (0x0F) +#define MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS (0x00) +#define MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY (0x01) +#define MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS (0x02) +#define MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER (0x03) +#define MPI2_RPY_DESCRIPT_FLAGS_RAID_ACCELERATOR_SUCCESS (0x05) +#define MPI2_RPY_DESCRIPT_FLAGS_UNUSED (0x0F) + +/* values for marking a reply descriptor as unused */ +#define MPI2_RPY_DESCRIPT_UNUSED_WORD0_MARK (0xFFFFFFFF) +#define MPI2_RPY_DESCRIPT_UNUSED_WORD1_MARK (0xFFFFFFFF) + +struct mfi_mpi2_reply_default { + struct mfi_mpi2_reply_header header; + uint32_t DescriptorTypeDependent2; +}; + +struct mfi_mpi2_reply_address { + struct mfi_mpi2_reply_header header; + uint32_t ReplyFrameAddress; +}; + +struct mfi_mpi2_reply_scsi_io { + struct mfi_mpi2_reply_header header; + uint16_t TaskTag; /* 0x04 */ + uint16_t Reserved1; /* 0x06 */ +}; + +struct mfi_mpi2_reply_target_assist { + struct mfi_mpi2_reply_header header; + uint8_t SequenceNumber; /* 0x04 */ + uint8_t Reserved1; /* 0x04 */ + uint16_t IoIndex; /* 0x06 */ +}; + +struct mfi_mpi2_reply_target_cmd_buffer { + struct mfi_mpi2_reply_header header; + uint8_t SequenceNumber; /* 0x04 */ + uint8_t Flags; /* 0x04 */ + uint16_t InitiatorDevHandle; /* 0x06 */ + uint16_t IoIndex; /* 0x06 */ +}; + +struct mfi_mpi2_reply_raid_accel { + struct mfi_mpi2_reply_header header; + uint8_t SequenceNumber; /* 0x04 */ + uint32_t Reserved; /* 0x04 */ +}; + +/* union of Reply Descriptors */ +union mfi_mpi2_reply_descriptor { + struct mfi_mpi2_reply_header header; + struct mfi_mpi2_reply_scsi_io scsi_io; + struct mfi_mpi2_reply_target_assist target_assist; + struct mfi_mpi2_reply_target_cmd_buffer target_cmd; + struct mfi_mpi2_reply_raid_accel raid_accel; + struct mfi_mpi2_reply_default reply_default; + uint64_t words; +}; + +struct IO_REQUEST_INFO { + uint64_t ldStartBlock; + uint32_t numBlocks; + uint16_t ldTgtId; + uint8_t isRead; + uint16_t devHandle; + uint64_t pdBlock; + uint8_t fpOkForIo; +}; + #define MFI_SCSI_MAX_TARGETS 128 #define MFI_SCSI_MAX_LUNS 8 #define MFI_SCSI_INITIATOR_ID 255 diff --git a/sys/dev/mfi/mfivar.h b/sys/dev/mfi/mfivar.h index 9fcd0d9..061dfd5 100644 --- a/sys/dev/mfi/mfivar.h +++ b/sys/dev/mfi/mfivar.h @@ -59,6 +59,10 @@ __FBSDID("$FreeBSD$"); #include <sys/lock.h> #include <sys/sx.h> +#include <sys/types.h> +#include <sys/taskqueue.h> +#include <machine/atomic.h> + /* * SCSI structures and definitions are used from here, but no linking * requirements are made to CAM. @@ -70,6 +74,8 @@ struct mfi_hwcomms { uint32_t hw_ci; uint32_t hw_reply_q[1]; }; +#define MEGASAS_MAX_NAME 32 +#define MEGASAS_VERSION "4.23" struct mfi_softc; struct disk; @@ -80,9 +86,9 @@ struct mfi_command { time_t cm_timestamp; struct mfi_softc *cm_sc; union mfi_frame *cm_frame; - uint32_t cm_frame_busaddr; + bus_addr_t cm_frame_busaddr; struct mfi_sense *cm_sense; - uint32_t cm_sense_busaddr; + bus_addr_t cm_sense_busaddr; bus_dmamap_t cm_dmamap; union mfi_sgl *cm_sg; void *cm_data; @@ -101,6 +107,7 @@ struct mfi_command { #define MFI_ON_MFIQ_BUSY (1<<7) #define MFI_ON_MFIQ_MASK ((1<<5)|(1<<6)|(1<<7)) int cm_aen_abort; + uint8_t retry_for_fw_reset; void (* cm_complete)(struct mfi_command *cm); void *cm_private; int cm_index; @@ -120,11 +127,42 @@ struct mfi_disk { #define MFI_DISK_FLAGS_DISABLED 0x02 }; +struct mfi_system_pd { + TAILQ_ENTRY(mfi_system_pd) pd_link; + device_t pd_dev; + int pd_id; + int pd_unit; + struct mfi_softc *pd_controller; + struct mfi_pd_info *pd_info; + struct disk *pd_disk; + int pd_flags; +}; + +struct mfi_evt_queue_elm { + TAILQ_ENTRY(mfi_evt_queue_elm) link; + struct mfi_evt_detail detail; +}; + struct mfi_aen { TAILQ_ENTRY(mfi_aen) aen_link; struct proc *p; }; +struct mfi_skinny_dma_info { + bus_dma_tag_t dmat[514]; + bus_dmamap_t dmamap[514]; + uint32_t mem[514]; + int noofmaps; +}; + +struct mfi_cmd_tbolt; +typedef struct { + volatile unsigned int val; +} atomic_t; + +#define atomic_read(v) ((v)->val) +#define atomic_set(v,i) ((v)->val - (i)) + struct mfi_softc { device_t mfi_dev; int mfi_flags; @@ -135,11 +173,22 @@ struct mfi_softc { #define MFI_FLAGS_1064R (1<<4) #define MFI_FLAGS_1078 (1<<5) #define MFI_FLAGS_GEN2 (1<<6) +#define MFI_FLAGS_SKINNY (1<<7) +#define MFI_FLAGS_TBOLT (1<<8) + // Start: LSIP200113393 + bus_dma_tag_t verbuf_h_dmat; + bus_dmamap_t verbuf_h_dmamap; + uint32_t verbuf_h_busaddr; + uint32_t *verbuf; + void *kbuff_arr[MAX_IOCTL_SGE]; + bus_dma_tag_t mfi_kbuff_arr_dmat[2]; + bus_dmamap_t mfi_kbuff_arr_dmamap[2]; + bus_addr_t mfi_kbuff_arr_busaddr[2]; struct mfi_hwcomms *mfi_comms; TAILQ_HEAD(,mfi_command) mfi_free; TAILQ_HEAD(,mfi_command) mfi_ready; - TAILQ_HEAD(,mfi_command) mfi_busy; + TAILQ_HEAD(BUSYQ,mfi_command) mfi_busy; struct bio_queue_head mfi_bioq; struct mfi_qstat mfi_qstat[MFIQ_COUNT]; @@ -153,17 +202,27 @@ struct mfi_softc { bus_dma_tag_t mfi_comms_dmat; bus_dmamap_t mfi_comms_dmamap; - uint32_t mfi_comms_busaddr; + bus_addr_t mfi_comms_busaddr; bus_dma_tag_t mfi_frames_dmat; bus_dmamap_t mfi_frames_dmamap; - uint32_t mfi_frames_busaddr; + bus_addr_t mfi_frames_busaddr; union mfi_frame *mfi_frames; + bus_dma_tag_t mfi_tb_init_dmat; + bus_dmamap_t mfi_tb_init_dmamap; + bus_addr_t mfi_tb_init_busaddr; + bus_addr_t mfi_tb_ioc_init_busaddr; + union mfi_frame *mfi_tb_init; + + TAILQ_HEAD(,mfi_evt_queue_elm) mfi_evt_queue; + struct task mfi_evt_task; TAILQ_HEAD(,mfi_aen) mfi_aen_pids; struct mfi_command *mfi_aen_cm; + struct mfi_command *mfi_skinny_cm; uint32_t mfi_aen_triggered; uint32_t mfi_poll_waiting; + uint32_t mfi_boot_seq_num; struct selinfo mfi_select; int mfi_delete_busy_volumes; int mfi_keep_deleted_volumes; @@ -180,6 +239,14 @@ struct mfi_softc { struct intr_config_hook mfi_ich; eventhandler_tag eh; + /* OCR flags */ + atomic_t fw_reset_no_pci_access; + uint8_t adpreset; + uint8_t issuepend_done; + uint8_t disableOnlineCtrlReset; + uint32_t mfiStatus; + uint32_t last_seq_num; + uint32_t volatile hw_crit_error; /* * Allocation for the command array. Used as an indexable array to @@ -196,7 +263,7 @@ struct mfi_softc { */ int mfi_max_fw_cmds; /* - * How many S/G elements we'll ever actually use + * How many S/G elements we'll ever actually use */ int mfi_max_sge; /* @@ -215,6 +282,7 @@ struct mfi_softc { uint32_t mfi_max_io; TAILQ_HEAD(,mfi_disk) mfi_ld_tqh; + TAILQ_HEAD(,mfi_system_pd) mfi_syspd_tqh; eventhandler_tag mfi_eh; struct cdev *mfi_cdev; @@ -226,9 +294,98 @@ struct mfi_softc { /* Controller type specific interfaces */ void (*mfi_enable_intr)(struct mfi_softc *sc); + void (*mfi_disable_intr)(struct mfi_softc *sc); int32_t (*mfi_read_fw_status)(struct mfi_softc *sc); int (*mfi_check_clear_intr)(struct mfi_softc *sc); - void (*mfi_issue_cmd)(struct mfi_softc *sc,uint32_t bus_add,uint32_t frame_cnt); + void (*mfi_issue_cmd)(struct mfi_softc *sc, bus_addr_t bus_add, + uint32_t frame_cnt); + int (*mfi_adp_reset)(struct mfi_softc *sc); + int (*mfi_adp_check_reset)(struct mfi_softc *sc); + + /* ThunderBolt */ + uint32_t mfi_tbolt; + uint32_t MFA_enabled; + uint64_t map_id; + struct mfi_command *map_update_cmd; + /* Single Reply structure size */ + uint16_t reply_size; + /* Singler message size. */ + uint16_t raid_io_msg_size; + TAILQ_HEAD(TB, mfi_cmd_tbolt) mfi_cmd_tbolt_tqh; + /* ThunderBolt base contiguous memory mapping. */ + bus_dma_tag_t mfi_tb_dmat; + bus_dmamap_t mfi_tb_dmamap; + bus_addr_t mfi_tb_busaddr; + /* ThunderBolt Contiguous DMA memory Mapping */ + uint8_t * request_message_pool; + uint8_t * request_message_pool_align; + uint8_t * request_desc_pool; + //uint32_t request_desc_busaddr; + bus_addr_t request_msg_busaddr; + bus_addr_t reply_frame_busaddr; + bus_addr_t sg_frame_busaddr; + /* ThunderBolt IOC Init Descriptor */ + bus_dma_tag_t mfi_tb_ioc_init_dmat; + bus_dmamap_t mfi_tb_ioc_init_dmamap; + uint8_t * mfi_tb_ioc_init_desc; + struct mfi_cmd_tbolt **mfi_cmd_pool_tbolt; + /* Virtual address of reply Frame Pool */ + struct mfi_mpi2_reply_header* reply_frame_pool; + struct mfi_mpi2_reply_header* reply_frame_pool_align; + + /* Last reply frame address */ + uint8_t * reply_pool_limit; + uint16_t last_reply_idx; + uint8_t max_SGEs_in_chain_message; + uint8_t max_SGEs_in_main_message; + uint8_t chain_offset_value_for_main_message; + uint8_t chain_offset_value_for_mpt_ptmsg; +}; + +union desc_value { + uint64_t word; + struct { + uint32_t low; + uint32_t high; + }u; +}; + +// TODO find the right definition +#define XXX_MFI_CMD_OP_INIT2 0x9 +/* + * Request descriptor types + */ +#define MFI_REQ_DESCRIPT_FLAGS_LD_IO 0x7 +#define MFI_REQ_DESCRIPT_FLAGS_MFA 0x1 +#define MFI_REQ_DESCRIPT_FLAGS_TYPE_SHIFT 0x1 +#define MFI_FUSION_FP_DEFAULT_TIMEOUT 0x14 +#define MFI_LOAD_BALANCE_FLAG 0x1 +#define MFI_DCMD_MBOX_PEND_FLAG 0x1 + +//#define MR_PROT_INFO_TYPE_CONTROLLER 0x08 +#define MEGASAS_SCSI_VARIABLE_LENGTH_CMD 0x7f +#define MEGASAS_SCSI_SERVICE_ACTION_READ32 0x9 +#define MEGASAS_SCSI_SERVICE_ACTION_WRITE32 0xB +#define MEGASAS_SCSI_ADDL_CDB_LEN 0x18 +#define MEGASAS_RD_WR_PROTECT_CHECK_ALL 0x20 +#define MEGASAS_RD_WR_PROTECT_CHECK_NONE 0x60 +#define MEGASAS_EEDPBLOCKSIZE 512 +struct mfi_cmd_tbolt { + union mfi_mpi2_request_descriptor *request_desc; + struct mfi_mpi2_request_raid_scsi_io *io_request; + bus_addr_t io_request_phys_addr; + bus_addr_t sg_frame_phys_addr; + bus_addr_t sense_phys_addr; + MPI2_SGE_IO_UNION *sg_frame; + uint8_t *sense; + TAILQ_ENTRY(mfi_cmd_tbolt) next; + /* + * Context for a MFI frame. + * Used to get the mfi cmd from list when a MFI cmd is completed + */ + uint32_t sync_cmd_idx; + uint16_t index; + uint8_t status; }; extern int mfi_attach(struct mfi_softc *); @@ -239,6 +396,30 @@ extern void mfi_disk_complete(struct bio *); extern int mfi_disk_disable(struct mfi_disk *); extern void mfi_disk_enable(struct mfi_disk *); extern int mfi_dump_blocks(struct mfi_softc *, int id, uint64_t, void *, int); +extern int mfi_syspd_disable(struct mfi_system_pd *); +extern void mfi_syspd_enable(struct mfi_system_pd *); +extern int mfi_dump_syspd_blocks(struct mfi_softc *, int id, uint64_t, void *, + int); +extern int mfi_transition_firmware(struct mfi_softc *sc); +extern int mfi_aen_setup(struct mfi_softc *sc, uint32_t seq_start); +extern void mfi_complete(struct mfi_softc *sc, struct mfi_command *cm); +extern int mfi_mapcmd(struct mfi_softc *sc,struct mfi_command *cm); +extern int mfi_wait_command(struct mfi_softc *sc, struct mfi_command *cm); +extern void mfi_tbolt_enable_intr_ppc(struct mfi_softc *); +extern void mfi_tbolt_disable_intr_ppc(struct mfi_softc *); +extern int32_t mfi_tbolt_read_fw_status_ppc(struct mfi_softc *); +extern int32_t mfi_tbolt_check_clear_intr_ppc(struct mfi_softc *); +extern void mfi_tbolt_issue_cmd_ppc(struct mfi_softc *, bus_addr_t, uint32_t); +extern void mfi_tbolt_init_globals(struct mfi_softc*); +extern uint32_t mfi_tbolt_get_memory_requirement(struct mfi_softc *); +extern int mfi_tbolt_init_desc_pool(struct mfi_softc *, uint8_t *, uint32_t); +extern int mfi_tbolt_init_MFI_queue(struct mfi_softc *); +extern void mfi_intr_tbolt(void *arg); +extern int mfi_tbolt_alloc_cmd(struct mfi_softc *sc); +extern int mfi_tbolt_send_frame(struct mfi_softc *sc, struct mfi_command *cm); +extern int mfi_tbolt_adp_reset(struct mfi_softc *sc); +extern int mfi_tbolt_reset(struct mfi_softc *sc); +extern int mfi_tbolt_sync_map_info(struct mfi_softc *sc); #define MFIQ_ADD(sc, qname) \ do { \ @@ -397,7 +578,11 @@ mfi_print_sense(struct mfi_softc *sc, void *sense) MALLOC_DECLARE(M_MFIBUF); SYSCTL_DECL(_hw_mfi); +#define MFI_RESET_WAIT_TIME 180 #define MFI_CMD_TIMEOUT 30 +#define MFI_SYS_PD_IO 0 +#define MFI_LD_IO 1 +#define SKINNY_MEMORY 0x02000000 #define MFI_MAXPHYS (128 * 1024) #ifdef MFI_DEBUG |