1227068Sambrisko /*-
2227068Sambrisko * Redistribution and use in source and binary forms, with or without
3227068Sambrisko * modification, are permitted provided that the following conditions
4227068Sambrisko * are met:
5227068Sambrisko *
6227068Sambrisko *            Copyright 1994-2009 The FreeBSD Project.
7227068Sambrisko *            All rights reserved.
8227068Sambrisko *
9227068Sambrisko * 1. Redistributions of source code must retain the above copyright
10227068Sambrisko *    notice, this list of conditions and the following disclaimer.
11227068Sambrisko * 2. Redistributions in binary form must reproduce the above copyright
12227068Sambrisko *    notice, this list of conditions and the following disclaimer in the
13227068Sambrisko *    documentation and/or other materials provided with the distribution.
14227068Sambrisko *
15227068Sambrisko *    THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT``AS IS'' AND
16227068Sambrisko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17227068Sambrisko * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18227068Sambrisko * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FREEBSD PROJECT OR
19227068Sambrisko * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20227068Sambrisko * EXEMPLARY,OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21227068Sambrisko * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22227068Sambrisko * PROFITS; OR BUSINESS INTERRUPTION)HOWEVER CAUSED AND ON ANY THEORY
23227068Sambrisko * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24227068Sambrisko * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25227068Sambrisko * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26227068Sambrisko *
27227068Sambrisko * The views and conclusions contained in the software and documentation
28227068Sambrisko * are those of the authors and should not be interpreted as representing
29227068Sambrisko * official policies,either expressed or implied, of the FreeBSD Project.
30227068Sambrisko */
31227068Sambrisko
32227068Sambrisko
33227068Sambrisko#include <sys/cdefs.h>
34233711Sambrisko__FBSDID("$FreeBSD$");
35227068Sambrisko
36227068Sambrisko#include "opt_mfi.h"
37227068Sambrisko
38227068Sambrisko#include <sys/param.h>
39227068Sambrisko#include <sys/types.h>
40227068Sambrisko#include <sys/kernel.h>
41227068Sambrisko#include <sys/selinfo.h>
42227068Sambrisko#include <sys/bus.h>
43227068Sambrisko#include <sys/conf.h>
44227068Sambrisko#include <sys/bio.h>
45227068Sambrisko#include <sys/ioccom.h>
46227068Sambrisko#include <sys/eventhandler.h>
47227068Sambrisko#include <sys/callout.h>
48227068Sambrisko#include <sys/uio.h>
49227068Sambrisko#include <machine/bus.h>
50233711Sambrisko#include <sys/sysctl.h>
51227068Sambrisko#include <sys/systm.h>
52227068Sambrisko#include <sys/malloc.h>
53227068Sambrisko
54227068Sambrisko#include <dev/mfi/mfireg.h>
55227068Sambrisko#include <dev/mfi/mfi_ioctl.h>
56227068Sambrisko#include <dev/mfi/mfivar.h>
57227068Sambrisko
58247369Ssmhstruct mfi_cmd_tbolt *mfi_tbolt_get_cmd(struct mfi_softc *sc, struct mfi_command *);
59227068Sambriskounion mfi_mpi2_request_descriptor *
60227068Sambriskomfi_tbolt_get_request_descriptor(struct mfi_softc *sc, uint16_t index);
61227068Sambriskovoid mfi_tbolt_complete_cmd(struct mfi_softc *sc);
62227068Sambriskoint mfi_tbolt_build_io(struct mfi_softc *sc, struct mfi_command *mfi_cmd,
63227068Sambrisko    struct mfi_cmd_tbolt *cmd);
64227068Sambriskounion mfi_mpi2_request_descriptor *mfi_tbolt_build_mpt_cmd(struct mfi_softc
65227068Sambrisko    *sc, struct mfi_command *cmd);
66227068Sambriskouint8_t
67227068Sambriskomfi_build_mpt_pass_thru(struct mfi_softc *sc, struct mfi_command *mfi_cmd);
68227068Sambriskounion mfi_mpi2_request_descriptor *mfi_build_and_issue_cmd(struct mfi_softc
69227068Sambrisko    *sc, struct mfi_command *mfi_cmd);
70227068Sambriskovoid mfi_tbolt_build_ldio(struct mfi_softc *sc, struct mfi_command *mfi_cmd,
71227068Sambrisko    struct mfi_cmd_tbolt *cmd);
72227068Sambriskostatic int mfi_tbolt_make_sgl(struct mfi_softc *sc, struct mfi_command
73227068Sambrisko    *mfi_cmd, pMpi25IeeeSgeChain64_t sgl_ptr, struct mfi_cmd_tbolt *cmd);
74227068Sambriskovoid
75227068Sambriskomap_tbolt_cmd_status(struct mfi_command *mfi_cmd, uint8_t status,
76227068Sambrisko     uint8_t ext_status);
77227068Sambriskostatic void mfi_issue_pending_cmds_again (struct mfi_softc *sc);
78227068Sambriskostatic void mfi_kill_hba (struct mfi_softc *sc);
79227068Sambriskostatic void mfi_process_fw_state_chg_isr(void *arg);
80235014Sambriskostatic void mfi_sync_map_complete(struct mfi_command *);
81235014Sambriskostatic void mfi_queue_map_sync(struct mfi_softc *sc);
82227068Sambrisko
83227068Sambrisko#define MFI_FUSION_ENABLE_INTERRUPT_MASK	(0x00000008)
84227068Sambrisko
85247369Ssmh
86247369Ssmhextern int	mfi_polled_cmd_timeout;
87247369Ssmhstatic int	mfi_fw_reset_test = 0;
88247369Ssmh#ifdef MFI_DEBUG
89247369SsmhTUNABLE_INT("hw.mfi.fw_reset_test", &mfi_fw_reset_test);
90247369SsmhSYSCTL_INT(_hw_mfi, OID_AUTO, fw_reset_test, CTLFLAG_RWTUN, &mfi_fw_reset_test,
91247369Ssmh           0, "Force a firmware reset condition");
92247369Ssmh#endif
93247369Ssmh
94227068Sambriskovoid
95227068Sambriskomfi_tbolt_enable_intr_ppc(struct mfi_softc *sc)
96227068Sambrisko{
97227068Sambrisko	MFI_WRITE4(sc, MFI_OMSK, ~MFI_FUSION_ENABLE_INTERRUPT_MASK);
98227068Sambrisko	MFI_READ4(sc, MFI_OMSK);
99227068Sambrisko}
100227068Sambrisko
101227068Sambriskovoid
102227068Sambriskomfi_tbolt_disable_intr_ppc(struct mfi_softc *sc)
103227068Sambrisko{
104227068Sambrisko	MFI_WRITE4(sc, MFI_OMSK, 0xFFFFFFFF);
105227068Sambrisko	MFI_READ4(sc, MFI_OMSK);
106227068Sambrisko}
107227068Sambrisko
108227068Sambriskoint32_t
109227068Sambriskomfi_tbolt_read_fw_status_ppc(struct mfi_softc *sc)
110227068Sambrisko{
111227068Sambrisko	return MFI_READ4(sc, MFI_OSP0);
112227068Sambrisko}
113227068Sambrisko
114227068Sambriskoint32_t
115227068Sambriskomfi_tbolt_check_clear_intr_ppc(struct mfi_softc *sc)
116227068Sambrisko{
117227068Sambrisko	int32_t status, mfi_status = 0;
118227068Sambrisko
119227068Sambrisko	status = MFI_READ4(sc, MFI_OSTS);
120227068Sambrisko
121233711Sambrisko	if (status & 1) {
122227068Sambrisko		MFI_WRITE4(sc, MFI_OSTS, status);
123227068Sambrisko		MFI_READ4(sc, MFI_OSTS);
124233711Sambrisko		if (status & MFI_STATE_CHANGE_INTERRUPT) {
125227068Sambrisko			mfi_status |= MFI_FIRMWARE_STATE_CHANGE;
126227068Sambrisko		}
127227068Sambrisko
128227068Sambrisko		return mfi_status;
129227068Sambrisko	}
130233711Sambrisko	if (!(status & MFI_FUSION_ENABLE_INTERRUPT_MASK))
131227068Sambrisko		return 1;
132227068Sambrisko
133227068Sambrisko	MFI_READ4(sc, MFI_OSTS);
134227068Sambrisko	return 0;
135227068Sambrisko}
136227068Sambrisko
137227068Sambrisko
138227068Sambriskovoid
139233711Sambriskomfi_tbolt_issue_cmd_ppc(struct mfi_softc *sc, bus_addr_t bus_add,
140227068Sambrisko   uint32_t frame_cnt)
141227068Sambrisko{
142227068Sambrisko	bus_add |= (MFI_REQ_DESCRIPT_FLAGS_MFA
143227068Sambrisko	    << MFI_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
144233711Sambrisko	MFI_WRITE4(sc, MFI_IQPL, (uint32_t)bus_add);
145233711Sambrisko	MFI_WRITE4(sc, MFI_IQPH, (uint32_t)((uint64_t)bus_add >> 32));
146227068Sambrisko}
147227068Sambrisko
148235016Sambrisko/*
149227068Sambrisko * mfi_tbolt_adp_reset - For controller reset
150227068Sambrisko * @regs: MFI register set
151227068Sambrisko */
152235016Sambriskoint
153235016Sambriskomfi_tbolt_adp_reset(struct mfi_softc *sc)
154227068Sambrisko{
155233711Sambrisko	int retry = 0, i = 0;
156233711Sambrisko	int HostDiag;
157227068Sambrisko
158233711Sambrisko	MFI_WRITE4(sc, MFI_WSR, 0xF);
159233711Sambrisko	MFI_WRITE4(sc, MFI_WSR, 4);
160233711Sambrisko	MFI_WRITE4(sc, MFI_WSR, 0xB);
161233711Sambrisko	MFI_WRITE4(sc, MFI_WSR, 2);
162233711Sambrisko	MFI_WRITE4(sc, MFI_WSR, 7);
163233711Sambrisko	MFI_WRITE4(sc, MFI_WSR, 0xD);
164227068Sambrisko
165233711Sambrisko	for (i = 0; i < 10000; i++) ;
166227068Sambrisko
167233711Sambrisko	HostDiag = (uint32_t)MFI_READ4(sc, MFI_HDR);
168227068Sambrisko
169233711Sambrisko	while (!( HostDiag & DIAG_WRITE_ENABLE)) {
170233711Sambrisko		for (i = 0; i < 1000; i++);
171233711Sambrisko		HostDiag = (uint32_t)MFI_READ4(sc, MFI_HDR);
172247369Ssmh		device_printf(sc->mfi_dev, "ADP_RESET_TBOLT: retry time=%d, "
173247369Ssmh		    "hostdiag=%#x\n", retry, HostDiag);
174227068Sambrisko
175233711Sambrisko		if (retry++ >= 100)
176233711Sambrisko			return 1;
177233711Sambrisko	}
178227068Sambrisko
179247369Ssmh	device_printf(sc->mfi_dev, "ADP_RESET_TBOLT: HostDiag=%#x\n", HostDiag);
180227068Sambrisko
181233711Sambrisko	MFI_WRITE4(sc, MFI_HDR, (HostDiag | DIAG_RESET_ADAPTER));
182227068Sambrisko
183233711Sambrisko	for (i=0; i < 10; i++) {
184233711Sambrisko		for (i = 0; i < 10000; i++);
185233711Sambrisko	}
186227068Sambrisko
187233711Sambrisko	HostDiag = (uint32_t)MFI_READ4(sc, MFI_RSR);
188233711Sambrisko	while (HostDiag & DIAG_RESET_ADAPTER) {
189233711Sambrisko		for (i = 0; i < 1000; i++) ;
190233711Sambrisko		HostDiag = (uint32_t)MFI_READ4(sc, MFI_RSR);
191247369Ssmh		device_printf(sc->mfi_dev, "ADP_RESET_TBOLT: retry time=%d, "
192247369Ssmh		    "hostdiag=%#x\n", retry, HostDiag);
193227068Sambrisko
194233711Sambrisko		if (retry++ >= 1000)
195233711Sambrisko			return 1;
196233711Sambrisko	}
197233711Sambrisko	return 0;
198227068Sambrisko}
199227068Sambrisko
200227068Sambrisko/*
201235016Sambrisko * This routine initialize Thunderbolt specific device information
202227068Sambrisko */
203235016Sambriskovoid
204235016Sambriskomfi_tbolt_init_globals(struct mfi_softc *sc)
205227068Sambrisko{
206227068Sambrisko	/* Initialize single reply size and Message size */
207227068Sambrisko	sc->reply_size = MEGASAS_THUNDERBOLT_REPLY_SIZE;
208227068Sambrisko	sc->raid_io_msg_size = MEGASAS_THUNDERBOLT_NEW_MSG_SIZE;
209227068Sambrisko
210227068Sambrisko	/*
211227068Sambrisko	 * Calculating how many SGEs allowed in a allocated main message
212227068Sambrisko	 * (size of the Message - Raid SCSI IO message size(except SGE))
213227068Sambrisko	 * / size of SGE
214227068Sambrisko	 * (0x100 - (0x90 - 0x10)) / 0x10 = 8
215227068Sambrisko	 */
216227068Sambrisko	sc->max_SGEs_in_main_message =
217227068Sambrisko	    (uint8_t)((sc->raid_io_msg_size
218227068Sambrisko	    - (sizeof(struct mfi_mpi2_request_raid_scsi_io)
219227068Sambrisko	    - sizeof(MPI2_SGE_IO_UNION))) / sizeof(MPI2_SGE_IO_UNION));
220227068Sambrisko	/*
221227068Sambrisko	 * (Command frame size allocaed in SRB ext - Raid SCSI IO message size)
222227068Sambrisko	 * / size of SGL ;
223227068Sambrisko	 * (1280 - 256) / 16 = 64
224227068Sambrisko	 */
225227068Sambrisko	sc->max_SGEs_in_chain_message = (MR_COMMAND_SIZE
226227068Sambrisko	    - sc->raid_io_msg_size) / sizeof(MPI2_SGE_IO_UNION);
227227068Sambrisko	/*
228227068Sambrisko	 * (0x08-1) + 0x40 = 0x47 - 0x01 = 0x46  one is left for command
229227068Sambrisko	 * colscing
230227068Sambrisko	*/
231227068Sambrisko	sc->mfi_max_sge = (sc->max_SGEs_in_main_message - 1)
232227068Sambrisko	    + sc->max_SGEs_in_chain_message - 1;
233227068Sambrisko	/*
234227068Sambrisko	* This is the offset in number of 4 * 32bit words to the next chain
235227068Sambrisko	* (0x100 - 0x10)/0x10 = 0xF(15)
236227068Sambrisko	*/
237227068Sambrisko	sc->chain_offset_value_for_main_message = (sc->raid_io_msg_size
238227068Sambrisko	    - sizeof(MPI2_SGE_IO_UNION))/16;
239227068Sambrisko	sc->chain_offset_value_for_mpt_ptmsg
240227068Sambrisko	    = offsetof(struct mfi_mpi2_request_raid_scsi_io, SGL)/16;
241227068Sambrisko	sc->mfi_cmd_pool_tbolt = NULL;
242227068Sambrisko	sc->request_desc_pool = NULL;
243227068Sambrisko}
244227068Sambrisko
245227068Sambrisko/*
246235016Sambrisko * This function calculates the memory requirement for Thunderbolt
247235016Sambrisko * controller, returns the total required memory in bytes
248227068Sambrisko */
249227068Sambrisko
250235016Sambriskouint32_t
251235016Sambriskomfi_tbolt_get_memory_requirement(struct mfi_softc *sc)
252227068Sambrisko{
253227068Sambrisko	uint32_t size;
254233711Sambrisko	size = MEGASAS_THUNDERBOLT_MSG_ALLIGNMENT;	/* for Alignment */
255227068Sambrisko	size += sc->raid_io_msg_size * (sc->mfi_max_fw_cmds + 1);
256227068Sambrisko	size += sc->reply_size * sc->mfi_max_fw_cmds;
257233711Sambrisko	/* this is for SGL's */
258227068Sambrisko	size += MEGASAS_MAX_SZ_CHAIN_FRAME * sc->mfi_max_fw_cmds;
259227068Sambrisko	return size;
260227068Sambrisko}
261227068Sambrisko
262227068Sambrisko/*
263227068Sambrisko * Description:
264227068Sambrisko *      This function will prepare message pools for the Thunderbolt controller
265227068Sambrisko * Arguments:
266227068Sambrisko *      DevExt - HBA miniport driver's adapter data storage structure
267227068Sambrisko *      pMemLocation - start of the memory allocated for Thunderbolt.
268227068Sambrisko * Return Value:
269227068Sambrisko *      TRUE if successful
270227068Sambrisko *      FALSE if failed
271227068Sambrisko */
272235016Sambriskoint
273235016Sambriskomfi_tbolt_init_desc_pool(struct mfi_softc *sc, uint8_t* mem_location,
274227068Sambrisko    uint32_t tbolt_contg_length)
275227068Sambrisko{
276227068Sambrisko	uint32_t     offset = 0;
277227068Sambrisko	uint8_t      *addr = mem_location;
278227068Sambrisko
279227068Sambrisko	/* Request Descriptor Base physical Address */
280227068Sambrisko
281227068Sambrisko	/* For Request Decriptors Virtual Memory */
282227068Sambrisko	/* Initialise the aligned IO Frames Virtual Memory Pointer */
283233711Sambrisko	if (((uintptr_t)addr) & (0xFF)) {
284227068Sambrisko		addr = &addr[sc->raid_io_msg_size];
285227068Sambrisko		addr = (uint8_t *)((uintptr_t)addr & (~0xFF));
286227068Sambrisko		sc->request_message_pool_align = addr;
287227068Sambrisko	} else
288227068Sambrisko		sc->request_message_pool_align = addr;
289227068Sambrisko
290227068Sambrisko	offset = sc->request_message_pool_align - sc->request_message_pool;
291227068Sambrisko	sc->request_msg_busaddr = sc->mfi_tb_busaddr + offset;
292227068Sambrisko
293227068Sambrisko	/* DJA XXX should this be bus dma ??? */
294227068Sambrisko	/* Skip request message pool */
295227068Sambrisko	addr = &addr[sc->raid_io_msg_size * (sc->mfi_max_fw_cmds + 1)];
296227068Sambrisko	/* Reply Frame Pool is initialized */
297227068Sambrisko	sc->reply_frame_pool = (struct mfi_mpi2_reply_header *) addr;
298233711Sambrisko	if (((uintptr_t)addr) & (0xFF)) {
299227068Sambrisko		addr = &addr[sc->reply_size];
300227068Sambrisko		addr = (uint8_t *)((uintptr_t)addr & (~0xFF));
301227068Sambrisko	}
302227068Sambrisko	sc->reply_frame_pool_align
303227068Sambrisko		    = (struct mfi_mpi2_reply_header *)addr;
304227068Sambrisko
305227068Sambrisko	offset = (uintptr_t)sc->reply_frame_pool_align
306227068Sambrisko	    - (uintptr_t)sc->request_message_pool;
307227068Sambrisko	sc->reply_frame_busaddr = sc->mfi_tb_busaddr + offset;
308227068Sambrisko
309227068Sambrisko	/* Skip Reply Frame Pool */
310227068Sambrisko	addr += sc->reply_size * sc->mfi_max_fw_cmds;
311227068Sambrisko	sc->reply_pool_limit = addr;
312227068Sambrisko
313227068Sambrisko	/* initializing reply address to 0xFFFFFFFF */
314227068Sambrisko	memset((uint8_t *)sc->reply_frame_pool, 0xFF,
315227068Sambrisko	       (sc->reply_size * sc->mfi_max_fw_cmds));
316227068Sambrisko
317227068Sambrisko	offset = sc->reply_size * sc->mfi_max_fw_cmds;
318227068Sambrisko	sc->sg_frame_busaddr = sc->reply_frame_busaddr + offset;
319227068Sambrisko	/* initialize the last_reply_idx to 0 */
320227068Sambrisko	sc->last_reply_idx = 0;
321247369Ssmh	MFI_WRITE4(sc, MFI_RFPI, sc->mfi_max_fw_cmds - 1);
322247369Ssmh	MFI_WRITE4(sc, MFI_RPI, sc->last_reply_idx);
323227068Sambrisko	offset = (sc->sg_frame_busaddr + (MEGASAS_MAX_SZ_CHAIN_FRAME *
324227068Sambrisko	    sc->mfi_max_fw_cmds)) - sc->mfi_tb_busaddr;
325233711Sambrisko	if (offset > tbolt_contg_length)
326233711Sambrisko		device_printf(sc->mfi_dev, "Error:Initialized more than "
327227068Sambrisko		    "allocated\n");
328227068Sambrisko	return 0;
329227068Sambrisko}
330227068Sambrisko
331227068Sambrisko/*
332235016Sambrisko * This routine prepare and issue INIT2 frame to the Firmware
333227068Sambrisko */
334227068Sambrisko
335227068Sambriskoint
336227068Sambriskomfi_tbolt_init_MFI_queue(struct mfi_softc *sc)
337227068Sambrisko{
338227068Sambrisko	struct MPI2_IOC_INIT_REQUEST   *mpi2IocInit;
339247369Ssmh	struct mfi_init_frame		*mfi_init;
340227068Sambrisko	uintptr_t			offset = 0;
341233711Sambrisko	bus_addr_t			phyAddress;
342227068Sambrisko	MFI_ADDRESS			*mfiAddressTemp;
343247369Ssmh	struct mfi_command		*cm, cmd_tmp;
344227068Sambrisko	int error;
345227068Sambrisko
346247369Ssmh	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
347247369Ssmh
348227068Sambrisko	/* Check if initialization is already completed */
349233711Sambrisko	if (sc->MFA_enabled) {
350247369Ssmh		device_printf(sc->mfi_dev, "tbolt_init already initialised!\n");
351227068Sambrisko		return 1;
352227068Sambrisko	}
353227068Sambrisko
354227068Sambrisko	if ((cm = mfi_dequeue_free(sc)) == NULL) {
355247369Ssmh		device_printf(sc->mfi_dev, "tbolt_init failed to get command "
356247369Ssmh		    " entry!\n");
357227068Sambrisko		return (EBUSY);
358227068Sambrisko	}
359247369Ssmh
360247369Ssmh	cmd_tmp.cm_frame = cm->cm_frame;
361247369Ssmh	cmd_tmp.cm_frame_busaddr = cm->cm_frame_busaddr;
362247369Ssmh	cmd_tmp.cm_dmamap = cm->cm_dmamap;
363247369Ssmh
364227068Sambrisko	cm->cm_frame = (union mfi_frame *)((uintptr_t)sc->mfi_tb_init);
365227068Sambrisko	cm->cm_frame_busaddr = sc->mfi_tb_init_busaddr;
366227068Sambrisko	cm->cm_dmamap = sc->mfi_tb_init_dmamap;
367227068Sambrisko	cm->cm_frame->header.context = 0;
368227068Sambrisko
369227068Sambrisko	/*
370227068Sambrisko	 * Abuse the SG list area of the frame to hold the init_qinfo
371227068Sambrisko	 * object;
372227068Sambrisko	 */
373227068Sambrisko	mfi_init = &cm->cm_frame->init;
374227068Sambrisko
375247369Ssmh	mpi2IocInit = (struct MPI2_IOC_INIT_REQUEST *)sc->mfi_tb_ioc_init_desc;
376227068Sambrisko	bzero(mpi2IocInit, sizeof(struct MPI2_IOC_INIT_REQUEST));
377227068Sambrisko	mpi2IocInit->Function  = MPI2_FUNCTION_IOC_INIT;
378227068Sambrisko	mpi2IocInit->WhoInit   = MPI2_WHOINIT_HOST_DRIVER;
379227068Sambrisko
380227068Sambrisko	/* set MsgVersion and HeaderVersion host driver was built with */
381227068Sambrisko	mpi2IocInit->MsgVersion = MPI2_VERSION;
382227068Sambrisko	mpi2IocInit->HeaderVersion = MPI2_HEADER_VERSION;
383227068Sambrisko	mpi2IocInit->SystemRequestFrameSize = sc->raid_io_msg_size/4;
384227068Sambrisko	mpi2IocInit->ReplyDescriptorPostQueueDepth
385227068Sambrisko	    = (uint16_t)sc->mfi_max_fw_cmds;
386227068Sambrisko	mpi2IocInit->ReplyFreeQueueDepth = 0; /* Not supported by MR. */
387227068Sambrisko
388227068Sambrisko	/* Get physical address of reply frame pool */
389227068Sambrisko	offset = (uintptr_t) sc->reply_frame_pool_align
390227068Sambrisko	    - (uintptr_t)sc->request_message_pool;
391227068Sambrisko	phyAddress = sc->mfi_tb_busaddr + offset;
392227068Sambrisko	mfiAddressTemp =
393227068Sambrisko	    (MFI_ADDRESS *)&mpi2IocInit->ReplyDescriptorPostQueueAddress;
394233711Sambrisko	mfiAddressTemp->u.addressLow = (uint32_t)phyAddress;
395233711Sambrisko	mfiAddressTemp->u.addressHigh = (uint32_t)((uint64_t)phyAddress >> 32);
396227068Sambrisko
397227068Sambrisko	/* Get physical address of request message pool */
398227068Sambrisko	offset = sc->request_message_pool_align - sc->request_message_pool;
399227068Sambrisko	phyAddress =  sc->mfi_tb_busaddr + offset;
400227068Sambrisko	mfiAddressTemp = (MFI_ADDRESS *)&mpi2IocInit->SystemRequestFrameBaseAddress;
401233711Sambrisko	mfiAddressTemp->u.addressLow = (uint32_t)phyAddress;
402233711Sambrisko	mfiAddressTemp->u.addressHigh = (uint32_t)((uint64_t)phyAddress >> 32);
403233711Sambrisko	mpi2IocInit->ReplyFreeQueueAddress =  0; /* Not supported by MR. */
404227068Sambrisko	mpi2IocInit->TimeStamp = time_uptime;
405227068Sambrisko
406227068Sambrisko	if (sc->verbuf) {
407227068Sambrisko		snprintf((char *)sc->verbuf, strlen(MEGASAS_VERSION) + 2, "%s\n",
408227068Sambrisko                MEGASAS_VERSION);
409233711Sambrisko		mfi_init->driver_ver_lo = (uint32_t)sc->verbuf_h_busaddr;
410233711Sambrisko		mfi_init->driver_ver_hi =
411233711Sambrisko		    (uint32_t)((uint64_t)sc->verbuf_h_busaddr >> 32);
412227068Sambrisko	}
413227068Sambrisko	/* Get the physical address of the mpi2 ioc init command */
414227068Sambrisko	phyAddress =  sc->mfi_tb_ioc_init_busaddr;
415233711Sambrisko	mfi_init->qinfo_new_addr_lo = (uint32_t)phyAddress;
416233711Sambrisko	mfi_init->qinfo_new_addr_hi = (uint32_t)((uint64_t)phyAddress >> 32);
417227068Sambrisko	mfi_init->header.flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE;
418227068Sambrisko
419227068Sambrisko	mfi_init->header.cmd = MFI_CMD_INIT;
420227068Sambrisko	mfi_init->header.data_len = sizeof(struct MPI2_IOC_INIT_REQUEST);
421227068Sambrisko	mfi_init->header.cmd_status = MFI_STAT_INVALID_STATUS;
422227068Sambrisko
423227068Sambrisko	cm->cm_data = NULL;
424227068Sambrisko	cm->cm_flags |= MFI_CMD_POLLED;
425227068Sambrisko	cm->cm_timestamp = time_uptime;
426227068Sambrisko	if ((error = mfi_mapcmd(sc, cm)) != 0) {
427227068Sambrisko		device_printf(sc->mfi_dev, "failed to send IOC init2 "
428227068Sambrisko		    "command %d at %lx\n", error, (long)cm->cm_frame_busaddr);
429247369Ssmh		goto out;
430227068Sambrisko	}
431227068Sambrisko
432247369Ssmh	if (mfi_init->header.cmd_status == MFI_STAT_OK) {
433227068Sambrisko		sc->MFA_enabled = 1;
434247369Ssmh	} else {
435247369Ssmh		device_printf(sc->mfi_dev, "Init command Failed %#x\n",
436227068Sambrisko		    mfi_init->header.cmd_status);
437247369Ssmh		error = mfi_init->header.cmd_status;
438247369Ssmh		goto out;
439227068Sambrisko	}
440227068Sambrisko
441247369Ssmhout:
442247369Ssmh	cm->cm_frame = cmd_tmp.cm_frame;
443247369Ssmh	cm->cm_frame_busaddr = cmd_tmp.cm_frame_busaddr;
444247369Ssmh	cm->cm_dmamap = cmd_tmp.cm_dmamap;
445247369Ssmh	mfi_release_command(cm);
446227068Sambrisko
447247369Ssmh	return (error);
448247369Ssmh
449227068Sambrisko}
450227068Sambrisko
451235016Sambriskoint
452235016Sambriskomfi_tbolt_alloc_cmd(struct mfi_softc *sc)
453227068Sambrisko{
454227068Sambrisko	struct mfi_cmd_tbolt *cmd;
455233711Sambrisko	bus_addr_t io_req_base_phys;
456227068Sambrisko	uint8_t *io_req_base;
457233711Sambrisko	int i = 0, j = 0, offset = 0;
458227068Sambrisko
459227068Sambrisko	/*
460227068Sambrisko	 * sc->mfi_cmd_pool_tbolt is an array of struct mfi_cmd_tbolt pointers.
461227068Sambrisko	 * Allocate the dynamic array first and then allocate individual
462227068Sambrisko	 * commands.
463227068Sambrisko	 */
464227068Sambrisko	sc->request_desc_pool = malloc(sizeof(
465227068Sambrisko	    union mfi_mpi2_request_descriptor) * sc->mfi_max_fw_cmds,
466227068Sambrisko	    M_MFIBUF, M_NOWAIT|M_ZERO);
467247369Ssmh
468247369Ssmh	if (sc->request_desc_pool == NULL) {
469247369Ssmh		device_printf(sc->mfi_dev, "Could not alloc "
470247369Ssmh		    "memory for request_desc_pool\n");
471247369Ssmh		return (ENOMEM);
472247369Ssmh	}
473247369Ssmh
474227068Sambrisko	sc->mfi_cmd_pool_tbolt = malloc(sizeof(struct mfi_cmd_tbolt*)
475227068Sambrisko	    * sc->mfi_max_fw_cmds, M_MFIBUF, M_NOWAIT|M_ZERO);
476227068Sambrisko
477247369Ssmh	if (sc->mfi_cmd_pool_tbolt == NULL) {
478247369Ssmh		free(sc->request_desc_pool, M_MFIBUF);
479247369Ssmh		device_printf(sc->mfi_dev, "Could not alloc "
480247369Ssmh		    "memory for cmd_pool_tbolt\n");
481247369Ssmh		return (ENOMEM);
482227068Sambrisko	}
483227068Sambrisko
484227068Sambrisko	for (i = 0; i < sc->mfi_max_fw_cmds; i++) {
485227068Sambrisko		sc->mfi_cmd_pool_tbolt[i] = malloc(sizeof(
486227068Sambrisko		    struct mfi_cmd_tbolt),M_MFIBUF, M_NOWAIT|M_ZERO);
487227068Sambrisko
488227068Sambrisko		if (!sc->mfi_cmd_pool_tbolt[i]) {
489247369Ssmh			device_printf(sc->mfi_dev, "Could not alloc "
490247369Ssmh			    "cmd_pool_tbolt entry\n");
491227068Sambrisko
492227068Sambrisko			for (j = 0; j < i; j++)
493227068Sambrisko				free(sc->mfi_cmd_pool_tbolt[j], M_MFIBUF);
494227068Sambrisko
495247369Ssmh			free(sc->request_desc_pool, M_MFIBUF);
496247369Ssmh			sc->request_desc_pool = NULL;
497227068Sambrisko			free(sc->mfi_cmd_pool_tbolt, M_MFIBUF);
498227068Sambrisko			sc->mfi_cmd_pool_tbolt = NULL;
499247369Ssmh
500247369Ssmh			return (ENOMEM);
501227068Sambrisko		}
502227068Sambrisko	}
503227068Sambrisko
504227068Sambrisko	/*
505227068Sambrisko	 * The first 256 bytes (SMID 0) is not used. Don't add to the cmd
506247369Ssmh	 * list
507227068Sambrisko	 */
508227068Sambrisko	io_req_base = sc->request_message_pool_align
509227068Sambrisko		+ MEGASAS_THUNDERBOLT_NEW_MSG_SIZE;
510227068Sambrisko	io_req_base_phys = sc->request_msg_busaddr
511227068Sambrisko		+ MEGASAS_THUNDERBOLT_NEW_MSG_SIZE;
512227068Sambrisko
513227068Sambrisko	/*
514227068Sambrisko	 * Add all the commands to command pool (instance->cmd_pool)
515227068Sambrisko	 */
516227068Sambrisko	/* SMID 0 is reserved. Set SMID/index from 1 */
517227068Sambrisko
518227068Sambrisko	for (i = 0; i < sc->mfi_max_fw_cmds; i++) {
519227068Sambrisko		cmd = sc->mfi_cmd_pool_tbolt[i];
520227068Sambrisko		offset = MEGASAS_THUNDERBOLT_NEW_MSG_SIZE * i;
521227068Sambrisko		cmd->index = i + 1;
522227068Sambrisko		cmd->request_desc = (union mfi_mpi2_request_descriptor *)
523227068Sambrisko		    (sc->request_desc_pool + i);
524227068Sambrisko		cmd->io_request = (struct mfi_mpi2_request_raid_scsi_io *)
525227068Sambrisko		    (io_req_base + offset);
526227068Sambrisko		cmd->io_request_phys_addr = io_req_base_phys + offset;
527227068Sambrisko		cmd->sg_frame = (MPI2_SGE_IO_UNION *)(sc->reply_pool_limit
528227068Sambrisko		    + i * MEGASAS_MAX_SZ_CHAIN_FRAME);
529227068Sambrisko		cmd->sg_frame_phys_addr = sc->sg_frame_busaddr + i
530227068Sambrisko		    * MEGASAS_MAX_SZ_CHAIN_FRAME;
531242681Sambrisko		cmd->sync_cmd_idx = sc->mfi_max_fw_cmds;
532227068Sambrisko
533227068Sambrisko		TAILQ_INSERT_TAIL(&(sc->mfi_cmd_tbolt_tqh), cmd, next);
534227068Sambrisko	}
535227068Sambrisko	return 0;
536227068Sambrisko}
537227068Sambrisko
538235016Sambriskoint
539235016Sambriskomfi_tbolt_reset(struct mfi_softc *sc)
540227068Sambrisko{
541227068Sambrisko	uint32_t fw_state;
542227068Sambrisko
543227068Sambrisko	mtx_lock(&sc->mfi_io_lock);
544233711Sambrisko	if (sc->hw_crit_error) {
545233711Sambrisko		device_printf(sc->mfi_dev, "HW CRITICAL ERROR\n");
546227068Sambrisko		mtx_unlock(&sc->mfi_io_lock);
547227068Sambrisko		return 1;
548227068Sambrisko	}
549227068Sambrisko
550233711Sambrisko	if (sc->mfi_flags & MFI_FLAGS_TBOLT) {
551227068Sambrisko		fw_state = sc->mfi_read_fw_status(sc);
552247369Ssmh		if ((fw_state & MFI_FWSTATE_FAULT) == MFI_FWSTATE_FAULT ||
553247369Ssmh		    mfi_fw_reset_test) {
554233711Sambrisko			if ((sc->disableOnlineCtrlReset == 0)
555233711Sambrisko			    && (sc->adpreset == 0)) {
556233711Sambrisko				device_printf(sc->mfi_dev, "Adapter RESET "
557227068Sambrisko				    "condition is detected\n");
558227068Sambrisko				sc->adpreset = 1;
559227068Sambrisko				sc->issuepend_done = 0;
560227068Sambrisko				sc->MFA_enabled = 0;
561227068Sambrisko				sc->last_reply_idx = 0;
562227068Sambrisko				mfi_process_fw_state_chg_isr((void *) sc);
563227068Sambrisko			}
564227068Sambrisko			mtx_unlock(&sc->mfi_io_lock);
565227068Sambrisko			return 0;
566227068Sambrisko		}
567227068Sambrisko	}
568227068Sambrisko	mtx_unlock(&sc->mfi_io_lock);
569227068Sambrisko	return 1;
570227068Sambrisko}
571227068Sambrisko
572227068Sambrisko/*
573227068Sambrisko * mfi_intr_tbolt - isr entry point
574227068Sambrisko */
575235016Sambriskovoid
576235016Sambriskomfi_intr_tbolt(void *arg)
577227068Sambrisko{
578227068Sambrisko	struct mfi_softc *sc = (struct mfi_softc *)arg;
579227068Sambrisko
580233711Sambrisko	if (sc->mfi_check_clear_intr(sc) == 1) {
581227068Sambrisko		return;
582227068Sambrisko	}
583233711Sambrisko	if (sc->mfi_detaching)
584227068Sambrisko		return;
585227068Sambrisko	mtx_lock(&sc->mfi_io_lock);
586227068Sambrisko	mfi_tbolt_complete_cmd(sc);
587247369Ssmh	sc->mfi_flags &= ~MFI_FLAGS_QFRZN;
588227068Sambrisko	mfi_startio(sc);
589227068Sambrisko	mtx_unlock(&sc->mfi_io_lock);
590227068Sambrisko	return;
591227068Sambrisko}
592227068Sambrisko
593235016Sambrisko/*
594227068Sambrisko * map_cmd_status -	Maps FW cmd status to OS cmd status
595227068Sambrisko * @cmd :		Pointer to cmd
596227068Sambrisko * @status :		status of cmd returned by FW
597227068Sambrisko * @ext_status :	ext status of cmd returned by FW
598227068Sambrisko */
599227068Sambrisko
600227068Sambriskovoid
601227068Sambriskomap_tbolt_cmd_status(struct mfi_command *mfi_cmd, uint8_t status,
602227068Sambrisko    uint8_t ext_status)
603227068Sambrisko{
604227068Sambrisko	switch (status) {
605247369Ssmh	case MFI_STAT_OK:
606247369Ssmh		mfi_cmd->cm_frame->header.cmd_status = MFI_STAT_OK;
607247369Ssmh		mfi_cmd->cm_frame->dcmd.header.cmd_status = MFI_STAT_OK;
608247369Ssmh		mfi_cmd->cm_error = MFI_STAT_OK;
609247369Ssmh		break;
610227068Sambrisko
611247369Ssmh	case MFI_STAT_SCSI_IO_FAILED:
612247369Ssmh	case MFI_STAT_LD_INIT_IN_PROGRESS:
613247369Ssmh		mfi_cmd->cm_frame->header.cmd_status = status;
614247369Ssmh		mfi_cmd->cm_frame->header.scsi_status = ext_status;
615247369Ssmh		mfi_cmd->cm_frame->dcmd.header.cmd_status = status;
616247369Ssmh		mfi_cmd->cm_frame->dcmd.header.scsi_status
617247369Ssmh		    = ext_status;
618247369Ssmh		break;
619227068Sambrisko
620247369Ssmh	case MFI_STAT_SCSI_DONE_WITH_ERROR:
621247369Ssmh		mfi_cmd->cm_frame->header.cmd_status = ext_status;
622247369Ssmh		mfi_cmd->cm_frame->dcmd.header.cmd_status = ext_status;
623247369Ssmh		break;
624227068Sambrisko
625247369Ssmh	case MFI_STAT_LD_OFFLINE:
626247369Ssmh	case MFI_STAT_DEVICE_NOT_FOUND:
627247369Ssmh		mfi_cmd->cm_frame->header.cmd_status = status;
628247369Ssmh		mfi_cmd->cm_frame->dcmd.header.cmd_status = status;
629247369Ssmh		break;
630227068Sambrisko
631247369Ssmh	default:
632247369Ssmh		mfi_cmd->cm_frame->header.cmd_status = status;
633247369Ssmh		mfi_cmd->cm_frame->dcmd.header.cmd_status = status;
634247369Ssmh		break;
635247369Ssmh	}
636227068Sambrisko}
637227068Sambrisko
638235016Sambrisko/*
639233711Sambrisko * mfi_tbolt_return_cmd -	Return a cmd to free command pool
640233711Sambrisko * @instance:		Adapter soft state
641247369Ssmh * @tbolt_cmd:		Tbolt command packet to be returned to free command pool
642247369Ssmh * @mfi_cmd:		Oning MFI command packe
643233711Sambrisko */
644247369Ssmhvoid
645247369Ssmhmfi_tbolt_return_cmd(struct mfi_softc *sc, struct mfi_cmd_tbolt *tbolt_cmd,
646247369Ssmh    struct mfi_command *mfi_cmd)
647233711Sambrisko{
648233711Sambrisko	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
649227068Sambrisko
650247369Ssmh	mfi_cmd->cm_flags &= ~MFI_CMD_TBOLT;
651247369Ssmh	mfi_cmd->cm_extra_frames = 0;
652247369Ssmh	tbolt_cmd->sync_cmd_idx = sc->mfi_max_fw_cmds;
653247369Ssmh
654247369Ssmh	TAILQ_INSERT_TAIL(&sc->mfi_cmd_tbolt_tqh, tbolt_cmd, next);
655233711Sambrisko}
656227068Sambrisko
657235014Sambriskovoid
658235014Sambriskomfi_tbolt_complete_cmd(struct mfi_softc *sc)
659227068Sambrisko{
660227068Sambrisko	struct mfi_mpi2_reply_header *desc, *reply_desc;
661247369Ssmh	struct mfi_command *cmd_mfi;	/* For MFA Cmds */
662227068Sambrisko	struct mfi_cmd_tbolt *cmd_tbolt;
663227068Sambrisko	uint16_t smid;
664227068Sambrisko	uint8_t reply_descript_type;
665227068Sambrisko	struct mfi_mpi2_request_raid_scsi_io  *scsi_io_req;
666227068Sambrisko	uint32_t status, extStatus;
667227068Sambrisko	uint16_t num_completed;
668227068Sambrisko	union desc_value val;
669247369Ssmh	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
670227068Sambrisko
671227068Sambrisko	desc = (struct mfi_mpi2_reply_header *)
672227068Sambrisko		((uintptr_t)sc->reply_frame_pool_align
673227068Sambrisko		+ sc->last_reply_idx * sc->reply_size);
674227068Sambrisko	reply_desc = desc;
675227068Sambrisko
676247369Ssmh	if (reply_desc == NULL) {
677227068Sambrisko		device_printf(sc->mfi_dev, "reply desc is NULL!!\n");
678247369Ssmh		return;
679247369Ssmh	}
680227068Sambrisko
681227068Sambrisko	reply_descript_type = reply_desc->ReplyFlags
682227068Sambrisko	     & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
683227068Sambrisko	if (reply_descript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
684227068Sambrisko		return;
685227068Sambrisko
686227068Sambrisko	num_completed = 0;
687227068Sambrisko	val.word = ((union mfi_mpi2_reply_descriptor *)desc)->words;
688227068Sambrisko
689227068Sambrisko	/* Read Reply descriptor */
690227068Sambrisko	while ((val.u.low != 0xFFFFFFFF) && (val.u.high != 0xFFFFFFFF)) {
691227068Sambrisko		smid = reply_desc->SMID;
692247369Ssmh		if (smid == 0 || smid > sc->mfi_max_fw_cmds) {
693247369Ssmh			device_printf(sc->mfi_dev, "smid is %d cannot "
694247369Ssmh			    "proceed - skipping\n", smid);
695247369Ssmh			goto next;
696227068Sambrisko		}
697227068Sambrisko		cmd_tbolt = sc->mfi_cmd_pool_tbolt[smid - 1];
698247369Ssmh		if (cmd_tbolt->sync_cmd_idx == sc->mfi_max_fw_cmds) {
699247369Ssmh			device_printf(sc->mfi_dev, "cmd_tbolt %p "
700247369Ssmh			    "has invalid sync_cmd_idx=%d - skipping\n",
701247369Ssmh			    cmd_tbolt, cmd_tbolt->sync_cmd_idx);
702247369Ssmh			goto next;
703247369Ssmh		}
704227068Sambrisko		cmd_mfi = &sc->mfi_commands[cmd_tbolt->sync_cmd_idx];
705227068Sambrisko		scsi_io_req = cmd_tbolt->io_request;
706227068Sambrisko
707227068Sambrisko		status = cmd_mfi->cm_frame->dcmd.header.cmd_status;
708227068Sambrisko		extStatus = cmd_mfi->cm_frame->dcmd.header.scsi_status;
709235014Sambrisko		map_tbolt_cmd_status(cmd_mfi, status, extStatus);
710227068Sambrisko
711247369Ssmh		/* mfi_tbolt_return_cmd is handled by mfi complete / return */
712247369Ssmh		if ((cmd_mfi->cm_flags & MFI_CMD_SCSI) != 0 &&
713242681Sambrisko		    (cmd_mfi->cm_flags & MFI_CMD_POLLED) != 0) {
714242681Sambrisko			/* polled LD/SYSPD IO command */
715242681Sambrisko			/* XXX mark okay for now DJA */
716242681Sambrisko			cmd_mfi->cm_frame->header.cmd_status = MFI_STAT_OK;
717247369Ssmh
718242681Sambrisko		} else {
719242681Sambrisko			/* remove command from busy queue if not polled */
720247369Ssmh			if ((cmd_mfi->cm_flags & MFI_ON_MFIQ_BUSY) != 0)
721247369Ssmh				mfi_remove_busy(cmd_mfi);
722242681Sambrisko
723242681Sambrisko			/* complete the command */
724242681Sambrisko			mfi_complete(sc, cmd_mfi);
725227068Sambrisko		}
726227068Sambrisko
727247369Ssmhnext:
728227068Sambrisko		sc->last_reply_idx++;
729227068Sambrisko		if (sc->last_reply_idx >= sc->mfi_max_fw_cmds) {
730227068Sambrisko			MFI_WRITE4(sc, MFI_RPI, sc->last_reply_idx);
731227068Sambrisko			sc->last_reply_idx = 0;
732227068Sambrisko		}
733247369Ssmh
734247369Ssmh		/* Set it back to all 0xfff */
735227068Sambrisko		((union mfi_mpi2_reply_descriptor*)desc)->words =
736227068Sambrisko			~((uint64_t)0x00);
737227068Sambrisko
738227068Sambrisko		num_completed++;
739227068Sambrisko
740227068Sambrisko		/* Get the next reply descriptor */
741227068Sambrisko		desc = (struct mfi_mpi2_reply_header *)
742227068Sambrisko		    ((uintptr_t)sc->reply_frame_pool_align
743227068Sambrisko		    + sc->last_reply_idx * sc->reply_size);
744227068Sambrisko		reply_desc = desc;
745227068Sambrisko		val.word = ((union mfi_mpi2_reply_descriptor*)desc)->words;
746227068Sambrisko		reply_descript_type = reply_desc->ReplyFlags
747227068Sambrisko		    & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
748233711Sambrisko		if (reply_descript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
749227068Sambrisko			break;
750227068Sambrisko	}
751227068Sambrisko
752227068Sambrisko	if (!num_completed)
753227068Sambrisko		return;
754227068Sambrisko
755227068Sambrisko	/* update replyIndex to FW */
756233711Sambrisko	if (sc->last_reply_idx)
757227068Sambrisko		MFI_WRITE4(sc, MFI_RPI, sc->last_reply_idx);
758227068Sambrisko
759227068Sambrisko	return;
760227068Sambrisko}
761227068Sambrisko
762235016Sambrisko/*
763227068Sambrisko * mfi_get_cmd -	Get a command from the free pool
764227068Sambrisko * @instance:		Adapter soft state
765227068Sambrisko *
766227068Sambrisko * Returns a free command from the pool
767227068Sambrisko */
768227068Sambrisko
769235016Sambriskostruct mfi_cmd_tbolt *
770247369Ssmhmfi_tbolt_get_cmd(struct mfi_softc *sc, struct mfi_command *mfi_cmd)
771227068Sambrisko{
772227068Sambrisko	struct mfi_cmd_tbolt *cmd = NULL;
773227068Sambrisko
774227068Sambrisko	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
775227068Sambrisko
776247369Ssmh	if ((cmd = TAILQ_FIRST(&sc->mfi_cmd_tbolt_tqh)) == NULL)
777247369Ssmh		return (NULL);
778227068Sambrisko	TAILQ_REMOVE(&sc->mfi_cmd_tbolt_tqh, cmd, next);
779227068Sambrisko	memset((uint8_t *)cmd->sg_frame, 0, MEGASAS_MAX_SZ_CHAIN_FRAME);
780227068Sambrisko	memset((uint8_t *)cmd->io_request, 0,
781227068Sambrisko	    MEGASAS_THUNDERBOLT_NEW_MSG_SIZE);
782247369Ssmh
783247369Ssmh	cmd->sync_cmd_idx = mfi_cmd->cm_index;
784247369Ssmh	mfi_cmd->cm_extra_frames = cmd->index; /* Frame count used as SMID */
785247369Ssmh	mfi_cmd->cm_flags |= MFI_CMD_TBOLT;
786247369Ssmh
787227068Sambrisko	return cmd;
788227068Sambrisko}
789227068Sambrisko
790227068Sambriskounion mfi_mpi2_request_descriptor *
791227068Sambriskomfi_tbolt_get_request_descriptor(struct mfi_softc *sc, uint16_t index)
792227068Sambrisko{
793227068Sambrisko	uint8_t *p;
794227068Sambrisko
795227068Sambrisko	if (index >= sc->mfi_max_fw_cmds) {
796227068Sambrisko		device_printf(sc->mfi_dev, "Invalid SMID (0x%x)request "
797227068Sambrisko		    "for descriptor\n", index);
798227068Sambrisko		return NULL;
799227068Sambrisko	}
800227068Sambrisko	p = sc->request_desc_pool + sizeof(union mfi_mpi2_request_descriptor)
801227068Sambrisko	    * index;
802227068Sambrisko	memset(p, 0, sizeof(union mfi_mpi2_request_descriptor));
803227068Sambrisko	return (union mfi_mpi2_request_descriptor *)p;
804227068Sambrisko}
805227068Sambrisko
806227068Sambrisko
807233711Sambrisko/* Used to build IOCTL cmd */
808227068Sambriskouint8_t
809227068Sambriskomfi_build_mpt_pass_thru(struct mfi_softc *sc, struct mfi_command *mfi_cmd)
810227068Sambrisko{
811227068Sambrisko	MPI25_IEEE_SGE_CHAIN64 *mpi25_ieee_chain;
812227068Sambrisko	struct mfi_mpi2_request_raid_scsi_io *io_req;
813227068Sambrisko	struct mfi_cmd_tbolt *cmd;
814227068Sambrisko
815247369Ssmh	cmd = mfi_tbolt_get_cmd(sc, mfi_cmd);
816227068Sambrisko	if (!cmd)
817227068Sambrisko		return EBUSY;
818227068Sambrisko	io_req = cmd->io_request;
819227068Sambrisko	mpi25_ieee_chain = (MPI25_IEEE_SGE_CHAIN64 *)&io_req->SGL.IeeeChain;
820227068Sambrisko
821227068Sambrisko	io_req->Function = MPI2_FUNCTION_PASSTHRU_IO_REQUEST;
822227068Sambrisko	io_req->SGLOffset0 = offsetof(struct mfi_mpi2_request_raid_scsi_io,
823227068Sambrisko	    SGL) / 4;
824227068Sambrisko	io_req->ChainOffset = sc->chain_offset_value_for_mpt_ptmsg;
825227068Sambrisko
826227068Sambrisko	mpi25_ieee_chain->Address = mfi_cmd->cm_frame_busaddr;
827227068Sambrisko
828227068Sambrisko	/*
829227068Sambrisko	  In MFI pass thru, nextChainOffset will always be zero to
830227068Sambrisko	  indicate the end of the chain.
831227068Sambrisko	*/
832227068Sambrisko	mpi25_ieee_chain->Flags= MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT
833227068Sambrisko		| MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR;
834227068Sambrisko
835227068Sambrisko	/* setting the length to the maximum length */
836227068Sambrisko	mpi25_ieee_chain->Length = 1024;
837227068Sambrisko
838227068Sambrisko	return 0;
839227068Sambrisko}
840227068Sambrisko
841227068Sambriskovoid
842227068Sambriskomfi_tbolt_build_ldio(struct mfi_softc *sc, struct mfi_command *mfi_cmd,
843227068Sambrisko    struct mfi_cmd_tbolt *cmd)
844227068Sambrisko{
845227068Sambrisko	uint32_t start_lba_lo = 0, start_lba_hi = 0, device_id;
846227068Sambrisko	struct mfi_mpi2_request_raid_scsi_io	*io_request;
847227068Sambrisko	struct IO_REQUEST_INFO io_info;
848227068Sambrisko
849227068Sambrisko	device_id = mfi_cmd->cm_frame->io.header.target_id;
850227068Sambrisko	io_request = cmd->io_request;
851227068Sambrisko	io_request->RaidContext.TargetID = device_id;
852227068Sambrisko	io_request->RaidContext.Status = 0;
853262967Smarkj	io_request->RaidContext.exStatus = 0;
854262967Smarkj	io_request->RaidContext.regLockFlags = 0;
855227068Sambrisko
856227068Sambrisko	start_lba_lo = mfi_cmd->cm_frame->io.lba_lo;
857227068Sambrisko	start_lba_hi = mfi_cmd->cm_frame->io.lba_hi;
858227068Sambrisko
859227068Sambrisko	memset(&io_info, 0, sizeof(struct IO_REQUEST_INFO));
860227068Sambrisko	io_info.ldStartBlock = ((uint64_t)start_lba_hi << 32) | start_lba_lo;
861227068Sambrisko	io_info.numBlocks = mfi_cmd->cm_frame->io.header.data_len;
862227068Sambrisko	io_info.ldTgtId = device_id;
863227068Sambrisko	if ((mfi_cmd->cm_frame->header.flags & MFI_FRAME_DIR_READ) ==
864227068Sambrisko	    MFI_FRAME_DIR_READ)
865227068Sambrisko		io_info.isRead = 1;
866227068Sambrisko
867242681Sambrisko	io_request->RaidContext.timeoutValue
868242681Sambrisko		= MFI_FUSION_FP_DEFAULT_TIMEOUT;
869242681Sambrisko	io_request->Function = MPI2_FUNCTION_LD_IO_REQUEST;
870242681Sambrisko	io_request->DevHandle = device_id;
871242681Sambrisko	cmd->request_desc->header.RequestFlags
872242681Sambrisko		= (MFI_REQ_DESCRIPT_FLAGS_LD_IO
873242681Sambrisko		   << MFI_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
874233711Sambrisko	if ((io_request->IoFlags == 6) && (io_info.numBlocks == 0))
875227068Sambrisko		io_request->RaidContext.RegLockLength = 0x100;
876227068Sambrisko	io_request->DataLength = mfi_cmd->cm_frame->io.header.data_len
877227068Sambrisko	    * MFI_SECTOR_LEN;
878227068Sambrisko}
879227068Sambrisko
880235016Sambriskoint
881235016Sambriskomfi_tbolt_build_io(struct mfi_softc *sc, struct mfi_command *mfi_cmd,
882235016Sambrisko    struct mfi_cmd_tbolt *cmd)
883227068Sambrisko{
884242681Sambrisko	struct mfi_mpi2_request_raid_scsi_io *io_request;
885227068Sambrisko	uint32_t sge_count;
886242681Sambrisko	uint8_t cdb_len;
887242681Sambrisko	int readop;
888242681Sambrisko	u_int64_t lba;
889227068Sambrisko
890242681Sambrisko	io_request = cmd->io_request;
891242681Sambrisko	if (!(mfi_cmd->cm_frame->header.cmd == MFI_CMD_LD_READ
892242681Sambrisko	      || mfi_cmd->cm_frame->header.cmd == MFI_CMD_LD_WRITE))
893242681Sambrisko		return 1;
894227068Sambrisko
895242681Sambrisko	mfi_tbolt_build_ldio(sc, mfi_cmd, cmd);
896227068Sambrisko
897242681Sambrisko	/* Convert to SCSI command CDB */
898242681Sambrisko	bzero(io_request->CDB.CDB32, sizeof(io_request->CDB.CDB32));
899242681Sambrisko	if (mfi_cmd->cm_frame->header.cmd == MFI_CMD_LD_WRITE)
900242681Sambrisko		readop = 0;
901242681Sambrisko	else
902242681Sambrisko		readop = 1;
903227068Sambrisko
904242681Sambrisko	lba =  mfi_cmd->cm_frame->io.lba_hi;
905242681Sambrisko	lba = (lba << 32) + mfi_cmd->cm_frame->io.lba_lo;
906242681Sambrisko	cdb_len = mfi_build_cdb(readop, 0, lba,
907242681Sambrisko	    mfi_cmd->cm_frame->io.header.data_len, io_request->CDB.CDB32);
908242681Sambrisko
909242681Sambrisko	/* Just the CDB length, rest of the Flags are zero */
910227068Sambrisko	io_request->IoFlags = cdb_len;
911227068Sambrisko
912227068Sambrisko	/*
913227068Sambrisko	 * Construct SGL
914227068Sambrisko	 */
915227068Sambrisko	sge_count = mfi_tbolt_make_sgl(sc, mfi_cmd,
916227068Sambrisko	    (pMpi25IeeeSgeChain64_t) &io_request->SGL, cmd);
917227068Sambrisko	if (sge_count > sc->mfi_max_sge) {
918227068Sambrisko		device_printf(sc->mfi_dev, "Error. sge_count (0x%x) exceeds "
919227068Sambrisko		    "max (0x%x) allowed\n", sge_count, sc->mfi_max_sge);
920227068Sambrisko		return 1;
921227068Sambrisko	}
922227068Sambrisko	io_request->RaidContext.numSGE = sge_count;
923227068Sambrisko	io_request->SGLFlags = MPI2_SGE_FLAGS_64_BIT_ADDRESSING;
924227068Sambrisko
925227068Sambrisko	if (mfi_cmd->cm_frame->header.cmd == MFI_CMD_LD_WRITE)
926227068Sambrisko		io_request->Control = MPI2_SCSIIO_CONTROL_WRITE;
927227068Sambrisko	else
928227068Sambrisko		io_request->Control = MPI2_SCSIIO_CONTROL_READ;
929227068Sambrisko
930227068Sambrisko	io_request->SGLOffset0 = offsetof(
931227068Sambrisko	    struct mfi_mpi2_request_raid_scsi_io, SGL)/4;
932227068Sambrisko
933227068Sambrisko	io_request->SenseBufferLowAddress = mfi_cmd->cm_sense_busaddr;
934227068Sambrisko	io_request->SenseBufferLength = MFI_SENSE_LEN;
935242681Sambrisko	io_request->RaidContext.Status = MFI_STAT_INVALID_STATUS;
936242681Sambrisko	io_request->RaidContext.exStatus = MFI_STAT_INVALID_STATUS;
937242681Sambrisko
938227068Sambrisko	return 0;
939227068Sambrisko}
940227068Sambrisko
941227068Sambrisko
942227068Sambriskostatic int
943227068Sambriskomfi_tbolt_make_sgl(struct mfi_softc *sc, struct mfi_command *mfi_cmd,
944233711Sambrisko		   pMpi25IeeeSgeChain64_t sgl_ptr, struct mfi_cmd_tbolt *cmd)
945227068Sambrisko{
946233711Sambrisko	uint8_t i, sg_processed, sg_to_process;
947227068Sambrisko	uint8_t sge_count, sge_idx;
948227068Sambrisko	union mfi_sgl *os_sgl;
949262967Smarkj	pMpi25IeeeSgeChain64_t sgl_end;
950227068Sambrisko
951227068Sambrisko	/*
952227068Sambrisko	 * Return 0 if there is no data transfer
953227068Sambrisko	 */
954227068Sambrisko	if (!mfi_cmd->cm_sg || !mfi_cmd->cm_len) {
955227068Sambrisko	 	device_printf(sc->mfi_dev, "Buffer empty \n");
956227068Sambrisko		return 0;
957227068Sambrisko	}
958227068Sambrisko	os_sgl = mfi_cmd->cm_sg;
959227068Sambrisko	sge_count = mfi_cmd->cm_frame->header.sg_count;
960227068Sambrisko
961227068Sambrisko	if (sge_count > sc->mfi_max_sge) {
962227068Sambrisko		device_printf(sc->mfi_dev, "sgl ptr %p sg_cnt %d \n",
963233711Sambrisko		    os_sgl, sge_count);
964227068Sambrisko		return sge_count;
965227068Sambrisko	}
966227068Sambrisko
967227068Sambrisko	if (sge_count > sc->max_SGEs_in_main_message)
968227068Sambrisko		/* One element to store the chain info */
969227068Sambrisko		sge_idx = sc->max_SGEs_in_main_message - 1;
970227068Sambrisko	else
971227068Sambrisko		sge_idx = sge_count;
972227068Sambrisko
973262967Smarkj	if (sc->mfi_flags & (MFI_FLAGS_INVADER | MFI_FLAGS_FURY)) {
974262967Smarkj		sgl_end = sgl_ptr + (sc->max_SGEs_in_main_message - 1);
975262967Smarkj		sgl_end->Flags = 0;
976262967Smarkj	}
977262967Smarkj
978227068Sambrisko	for (i = 0; i < sge_idx; i++) {
979227068Sambrisko		/*
980233711Sambrisko		 * For 32bit BSD we are getting 32 bit SGL's from OS
981233711Sambrisko		 * but FW only take 64 bit SGL's so copying from 32 bit
982233711Sambrisko		 * SGL's to 64.
983233711Sambrisko		 */
984227068Sambrisko		if (sc->mfi_flags & MFI_FLAGS_SKINNY) {
985227068Sambrisko			sgl_ptr->Length = os_sgl->sg_skinny[i].len;
986227068Sambrisko			sgl_ptr->Address = os_sgl->sg_skinny[i].addr;
987227068Sambrisko		} else {
988227068Sambrisko			sgl_ptr->Length = os_sgl->sg32[i].len;
989233711Sambrisko			sgl_ptr->Address = os_sgl->sg32[i].addr;
990227068Sambrisko		}
991262967Smarkj		if (i == sge_count - 1 &&
992262967Smarkj		    (sc->mfi_flags & (MFI_FLAGS_INVADER | MFI_FLAGS_FURY)))
993262967Smarkj			sgl_ptr->Flags = MPI25_IEEE_SGE_FLAGS_END_OF_LIST;
994262967Smarkj		else
995262967Smarkj			sgl_ptr->Flags = 0;
996227068Sambrisko		sgl_ptr++;
997227068Sambrisko		cmd->io_request->ChainOffset = 0;
998227068Sambrisko	}
999227068Sambrisko
1000227068Sambrisko	sg_processed = i;
1001227068Sambrisko
1002227068Sambrisko	if (sg_processed < sge_count) {
1003227068Sambrisko		pMpi25IeeeSgeChain64_t sg_chain;
1004227068Sambrisko		sg_to_process = sge_count - sg_processed;
1005227068Sambrisko		cmd->io_request->ChainOffset =
1006227068Sambrisko		    sc->chain_offset_value_for_main_message;
1007227068Sambrisko		sg_chain = sgl_ptr;
1008227068Sambrisko		/* Prepare chain element */
1009227068Sambrisko		sg_chain->NextChainOffset = 0;
1010262967Smarkj		if (sc->mfi_flags & (MFI_FLAGS_INVADER | MFI_FLAGS_FURY))
1011262967Smarkj			sg_chain->Flags = MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT;
1012262967Smarkj		else
1013262967Smarkj			sg_chain->Flags = MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT |
1014262967Smarkj			    MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR;
1015227068Sambrisko		sg_chain->Length =  (sizeof(MPI2_SGE_IO_UNION) *
1016227068Sambrisko		    (sge_count - sg_processed));
1017233711Sambrisko		sg_chain->Address = cmd->sg_frame_phys_addr;
1018227068Sambrisko		sgl_ptr = (pMpi25IeeeSgeChain64_t)cmd->sg_frame;
1019227068Sambrisko		for (; i < sge_count; i++) {
1020227068Sambrisko			if (sc->mfi_flags & MFI_FLAGS_SKINNY) {
1021227068Sambrisko				sgl_ptr->Length = os_sgl->sg_skinny[i].len;
1022227068Sambrisko				sgl_ptr->Address = os_sgl->sg_skinny[i].addr;
1023233711Sambrisko			} else {
1024227068Sambrisko				sgl_ptr->Length = os_sgl->sg32[i].len;
1025233711Sambrisko				sgl_ptr->Address = os_sgl->sg32[i].addr;
1026227068Sambrisko			}
1027262967Smarkj			if (i == sge_count - 1 &&
1028262967Smarkj			    (sc->mfi_flags &
1029262967Smarkj			    (MFI_FLAGS_INVADER | MFI_FLAGS_FURY)))
1030262967Smarkj				sgl_ptr->Flags =
1031262967Smarkj				    MPI25_IEEE_SGE_FLAGS_END_OF_LIST;
1032262967Smarkj			else
1033262967Smarkj				sgl_ptr->Flags = 0;
1034227068Sambrisko			sgl_ptr++;
1035227068Sambrisko		}
1036227068Sambrisko	}
1037227068Sambrisko	return sge_count;
1038227068Sambrisko}
1039227068Sambrisko
1040227068Sambriskounion mfi_mpi2_request_descriptor *
1041227068Sambriskomfi_build_and_issue_cmd(struct mfi_softc *sc, struct mfi_command *mfi_cmd)
1042227068Sambrisko{
1043227068Sambrisko	struct mfi_cmd_tbolt *cmd;
1044227068Sambrisko	union mfi_mpi2_request_descriptor *req_desc = NULL;
1045227068Sambrisko	uint16_t index;
1046247369Ssmh	cmd = mfi_tbolt_get_cmd(sc, mfi_cmd);
1047247369Ssmh	if (cmd == NULL)
1048247369Ssmh		return (NULL);
1049227068Sambrisko
1050227068Sambrisko	index = cmd->index;
1051227068Sambrisko	req_desc = mfi_tbolt_get_request_descriptor(sc, index-1);
1052247369Ssmh	if (req_desc == NULL) {
1053247369Ssmh		mfi_tbolt_return_cmd(sc, cmd, mfi_cmd);
1054247369Ssmh		return (NULL);
1055247369Ssmh	}
1056247369Ssmh
1057247369Ssmh	if (mfi_tbolt_build_io(sc, mfi_cmd, cmd) != 0) {
1058247369Ssmh		mfi_tbolt_return_cmd(sc, cmd, mfi_cmd);
1059247369Ssmh		return (NULL);
1060247369Ssmh	}
1061227068Sambrisko	req_desc->header.SMID = index;
1062227068Sambrisko	return req_desc;
1063227068Sambrisko}
1064227068Sambrisko
1065227068Sambriskounion mfi_mpi2_request_descriptor *
1066227068Sambriskomfi_tbolt_build_mpt_cmd(struct mfi_softc *sc, struct mfi_command *cmd)
1067227068Sambrisko{
1068227068Sambrisko	union mfi_mpi2_request_descriptor *req_desc = NULL;
1069227068Sambrisko	uint16_t index;
1070227068Sambrisko	if (mfi_build_mpt_pass_thru(sc, cmd)) {
1071227068Sambrisko		device_printf(sc->mfi_dev, "Couldn't build MFI pass thru "
1072227068Sambrisko		    "cmd\n");
1073227068Sambrisko		return NULL;
1074227068Sambrisko	}
1075227068Sambrisko	/* For fusion the frame_count variable is used for SMID */
1076227068Sambrisko	index = cmd->cm_extra_frames;
1077227068Sambrisko
1078227068Sambrisko	req_desc = mfi_tbolt_get_request_descriptor(sc, index - 1);
1079247369Ssmh	if (req_desc == NULL)
1080227068Sambrisko		return NULL;
1081227068Sambrisko
1082237546Skevlo	bzero(req_desc, sizeof(*req_desc));
1083227068Sambrisko	req_desc->header.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO <<
1084227068Sambrisko	    MFI_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
1085227068Sambrisko	req_desc->header.SMID = index;
1086227068Sambrisko	return req_desc;
1087227068Sambrisko}
1088227068Sambrisko
1089227068Sambriskoint
1090227068Sambriskomfi_tbolt_send_frame(struct mfi_softc *sc, struct mfi_command *cm)
1091227068Sambrisko{
1092227068Sambrisko	struct mfi_frame_header *hdr;
1093227068Sambrisko	uint8_t *cdb;
1094227068Sambrisko	union mfi_mpi2_request_descriptor *req_desc = NULL;
1095247369Ssmh	int tm = mfi_polled_cmd_timeout * 1000;
1096227068Sambrisko
1097227068Sambrisko	hdr = &cm->cm_frame->header;
1098227068Sambrisko	cdb = cm->cm_frame->pass.cdb;
1099233711Sambrisko	if (sc->adpreset)
1100227068Sambrisko		return 1;
1101227068Sambrisko	if ((cm->cm_flags & MFI_CMD_POLLED) == 0) {
1102227068Sambrisko		cm->cm_timestamp = time_uptime;
1103227068Sambrisko		mfi_enqueue_busy(cm);
1104242681Sambrisko	} else {	/* still get interrupts for it */
1105235014Sambrisko		hdr->cmd_status = MFI_STAT_INVALID_STATUS;
1106227068Sambrisko		hdr->flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE;
1107227068Sambrisko	}
1108227068Sambrisko
1109227068Sambrisko	if (hdr->cmd == MFI_CMD_PD_SCSI_IO) {
1110227068Sambrisko		/* check for inquiry commands coming from CLI */
1111227068Sambrisko		if (cdb[0] != 0x28 || cdb[0] != 0x2A) {
1112227068Sambrisko			if ((req_desc = mfi_tbolt_build_mpt_cmd(sc, cm)) ==
1113227068Sambrisko			    NULL) {
1114227068Sambrisko				device_printf(sc->mfi_dev, "Mapping from MFI "
1115227068Sambrisko				    "to MPT Failed \n");
1116227068Sambrisko				return 1;
1117227068Sambrisko			}
1118227068Sambrisko		}
1119227068Sambrisko		else
1120227068Sambrisko			device_printf(sc->mfi_dev, "DJA NA XXX SYSPDIO\n");
1121242681Sambrisko	} else if (hdr->cmd == MFI_CMD_LD_SCSI_IO ||
1122227068Sambrisko	    hdr->cmd == MFI_CMD_LD_READ || hdr->cmd == MFI_CMD_LD_WRITE) {
1123242681Sambrisko		cm->cm_flags |= MFI_CMD_SCSI;
1124227068Sambrisko		if ((req_desc = mfi_build_and_issue_cmd(sc, cm)) == NULL) {
1125227068Sambrisko			device_printf(sc->mfi_dev, "LDIO Failed \n");
1126227068Sambrisko			return 1;
1127227068Sambrisko		}
1128242681Sambrisko	} else if ((req_desc = mfi_tbolt_build_mpt_cmd(sc, cm)) == NULL) {
1129247369Ssmh		device_printf(sc->mfi_dev, "Mapping from MFI to MPT Failed\n");
1130247369Ssmh		return (1);
1131242681Sambrisko	}
1132242681Sambrisko
1133242681Sambrisko	if (cm->cm_flags & MFI_CMD_SCSI) {
1134242681Sambrisko		/*
1135242681Sambrisko		 * LD IO needs to be posted since it doesn't get
1136242681Sambrisko		 * acknowledged via a status update so have the
1137242681Sambrisko		 * controller reply via mfi_tbolt_complete_cmd.
1138242681Sambrisko		 */
1139242681Sambrisko		hdr->flags &= ~MFI_FRAME_DONT_POST_IN_REPLY_QUEUE;
1140242681Sambrisko	}
1141242681Sambrisko
1142227068Sambrisko	MFI_WRITE4(sc, MFI_ILQP, (req_desc->words & 0xFFFFFFFF));
1143227068Sambrisko	MFI_WRITE4(sc, MFI_IHQP, (req_desc->words >>0x20));
1144227068Sambrisko
1145227068Sambrisko	if ((cm->cm_flags & MFI_CMD_POLLED) == 0)
1146227068Sambrisko		return 0;
1147227068Sambrisko
1148247369Ssmh	/*
1149247369Ssmh	 * This is a polled command, so busy-wait for it to complete.
1150247369Ssmh	 *
1151247369Ssmh	 * The value of hdr->cmd_status is updated directly by the hardware
1152247369Ssmh	 * so there is no garantee that mfi_tbolt_complete_cmd is called
1153247369Ssmh	 * prior to this value changing.
1154247369Ssmh	 */
1155235014Sambrisko	while (hdr->cmd_status == MFI_STAT_INVALID_STATUS) {
1156227068Sambrisko		DELAY(1000);
1157227068Sambrisko		tm -= 1;
1158227068Sambrisko		if (tm <= 0)
1159242681Sambrisko			break;
1160242681Sambrisko		if (cm->cm_flags & MFI_CMD_SCSI) {
1161247369Ssmh			/*
1162247369Ssmh			 * Force check reply queue.
1163247369Ssmh			 * This ensures that dump works correctly
1164247369Ssmh			 */
1165242681Sambrisko			mfi_tbolt_complete_cmd(sc);
1166242681Sambrisko		}
1167227068Sambrisko	}
1168235016Sambrisko
1169247369Ssmh	/* ensure the command cleanup has been processed before returning */
1170247369Ssmh	mfi_tbolt_complete_cmd(sc);
1171247369Ssmh
1172235014Sambrisko	if (hdr->cmd_status == MFI_STAT_INVALID_STATUS) {
1173227068Sambrisko		device_printf(sc->mfi_dev, "Frame %p timed out "
1174235014Sambrisko		    "command 0x%X\n", hdr, cm->cm_frame->dcmd.opcode);
1175227068Sambrisko		return (ETIMEDOUT);
1176227068Sambrisko	}
1177227068Sambrisko	return 0;
1178227068Sambrisko}
1179227068Sambrisko
1180235016Sambriskostatic void
1181247369Ssmhmfi_issue_pending_cmds_again(struct mfi_softc *sc)
1182227068Sambrisko{
1183233711Sambrisko	struct mfi_command *cm, *tmp;
1184247369Ssmh	struct mfi_cmd_tbolt *cmd;
1185227068Sambrisko
1186227068Sambrisko	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
1187233711Sambrisko	TAILQ_FOREACH_REVERSE_SAFE(cm, &sc->mfi_busy, BUSYQ, cm_link, tmp) {
1188227068Sambrisko
1189227068Sambrisko		cm->retry_for_fw_reset++;
1190227068Sambrisko
1191227068Sambrisko		/*
1192227068Sambrisko		 * If a command has continuously been tried multiple times
1193227068Sambrisko		 * and causing a FW reset condition, no further recoveries
1194227068Sambrisko		 * should be performed on the controller
1195227068Sambrisko		 */
1196227068Sambrisko		if (cm->retry_for_fw_reset == 3) {
1197247369Ssmh			device_printf(sc->mfi_dev, "megaraid_sas: command %p "
1198247369Ssmh			    "index=%d was tried multiple times during adapter "
1199247369Ssmh			    "reset - Shutting down the HBA\n", cm, cm->cm_index);
1200227068Sambrisko			mfi_kill_hba(sc);
1201227068Sambrisko			sc->hw_crit_error = 1;
1202227068Sambrisko			return;
1203227068Sambrisko		}
1204227068Sambrisko
1205247369Ssmh		mfi_remove_busy(cm);
1206247369Ssmh		if ((cm->cm_flags & MFI_CMD_TBOLT) != 0) {
1207247369Ssmh			if (cm->cm_extra_frames != 0 && cm->cm_extra_frames <=
1208247369Ssmh			    sc->mfi_max_fw_cmds) {
1209247369Ssmh				cmd = sc->mfi_cmd_pool_tbolt[cm->cm_extra_frames - 1];
1210247369Ssmh				mfi_tbolt_return_cmd(sc, cmd, cm);
1211247369Ssmh			} else {
1212247369Ssmh				device_printf(sc->mfi_dev,
1213247369Ssmh				    "Invalid extra_frames: %d detected\n",
1214247369Ssmh				    cm->cm_extra_frames);
1215227068Sambrisko			}
1216227068Sambrisko		}
1217247369Ssmh
1218247369Ssmh		if (cm->cm_frame->dcmd.opcode != MFI_DCMD_CTRL_EVENT_WAIT) {
1219247369Ssmh			device_printf(sc->mfi_dev,
1220247369Ssmh			    "APJ ****requeue command %p index=%d\n",
1221247369Ssmh			    cm, cm->cm_index);
1222247369Ssmh			mfi_requeue_ready(cm);
1223247369Ssmh		} else
1224247369Ssmh			mfi_release_command(cm);
1225227068Sambrisko	}
1226227068Sambrisko	mfi_startio(sc);
1227227068Sambrisko}
1228227068Sambrisko
1229235016Sambriskostatic void
1230247369Ssmhmfi_kill_hba(struct mfi_softc *sc)
1231227068Sambrisko{
1232227068Sambrisko	if (sc->mfi_flags & MFI_FLAGS_TBOLT)
1233247369Ssmh		MFI_WRITE4(sc, 0x00, MFI_STOP_ADP);
1234227068Sambrisko	else
1235247369Ssmh		MFI_WRITE4(sc, MFI_IDB, MFI_STOP_ADP);
1236227068Sambrisko}
1237227068Sambrisko
1238235016Sambriskostatic void
1239235016Sambriskomfi_process_fw_state_chg_isr(void *arg)
1240227068Sambrisko{
1241227068Sambrisko	struct mfi_softc *sc= (struct mfi_softc *)arg;
1242227068Sambrisko	int error, status;
1243227068Sambrisko
1244227068Sambrisko	if (sc->adpreset == 1) {
1245233711Sambrisko		device_printf(sc->mfi_dev, "First stage of FW reset "
1246227068Sambrisko		     "initiated...\n");
1247227068Sambrisko
1248227068Sambrisko		sc->mfi_adp_reset(sc);
1249227068Sambrisko		sc->mfi_enable_intr(sc);
1250227068Sambrisko
1251233711Sambrisko		device_printf(sc->mfi_dev, "First stage of reset complete, "
1252227068Sambrisko		    "second stage initiated...\n");
1253227068Sambrisko
1254227068Sambrisko		sc->adpreset = 2;
1255227068Sambrisko
1256227068Sambrisko		/* waiting for about 20 second before start the second init */
1257233711Sambrisko		for (int wait = 0; wait < 20000; wait++)
1258227068Sambrisko			DELAY(1000);
1259233711Sambrisko		device_printf(sc->mfi_dev, "Second stage of FW reset "
1260227068Sambrisko		     "initiated...\n");
1261233711Sambrisko		while ((status = MFI_READ4(sc, MFI_RSR)) & 0x04);
1262227068Sambrisko
1263227068Sambrisko		sc->mfi_disable_intr(sc);
1264227068Sambrisko
1265227068Sambrisko		/* We expect the FW state to be READY */
1266227068Sambrisko		if (mfi_transition_firmware(sc)) {
1267233711Sambrisko			device_printf(sc->mfi_dev, "controller is not in "
1268233711Sambrisko			    "ready state\n");
1269227068Sambrisko			mfi_kill_hba(sc);
1270247369Ssmh			sc->hw_crit_error = 1;
1271247369Ssmh			return;
1272227068Sambrisko		}
1273247369Ssmh		if ((error = mfi_tbolt_init_MFI_queue(sc)) != 0) {
1274247369Ssmh			device_printf(sc->mfi_dev, "Failed to initialise MFI "
1275247369Ssmh			    "queue\n");
1276247369Ssmh			mfi_kill_hba(sc);
1277247369Ssmh			sc->hw_crit_error = 1;
1278247369Ssmh			return;
1279247369Ssmh		}
1280227068Sambrisko
1281247369Ssmh		/* Init last reply index and max */
1282247369Ssmh		MFI_WRITE4(sc, MFI_RFPI, sc->mfi_max_fw_cmds - 1);
1283247369Ssmh		MFI_WRITE4(sc, MFI_RPI, sc->last_reply_idx);
1284227068Sambrisko
1285227068Sambrisko		sc->mfi_enable_intr(sc);
1286227068Sambrisko		sc->adpreset = 0;
1287247369Ssmh		if (sc->mfi_aen_cm != NULL) {
1288247369Ssmh			free(sc->mfi_aen_cm->cm_data, M_MFIBUF);
1289247369Ssmh			mfi_remove_busy(sc->mfi_aen_cm);
1290227068Sambrisko			mfi_release_command(sc->mfi_aen_cm);
1291227068Sambrisko			sc->mfi_aen_cm = NULL;
1292227068Sambrisko		}
1293247369Ssmh
1294247369Ssmh		if (sc->mfi_map_sync_cm != NULL) {
1295247369Ssmh			mfi_remove_busy(sc->mfi_map_sync_cm);
1296235014Sambrisko			mfi_release_command(sc->mfi_map_sync_cm);
1297235014Sambrisko			sc->mfi_map_sync_cm = NULL;
1298227068Sambrisko		}
1299227068Sambrisko		mfi_issue_pending_cmds_again(sc);
1300227068Sambrisko
1301227068Sambrisko		/*
1302227068Sambrisko		 * Issue pending command can result in adapter being marked
1303227068Sambrisko		 * dead because of too many re-tries. Check for that
1304227068Sambrisko		 * condition before clearing the reset condition on the FW
1305227068Sambrisko		 */
1306227068Sambrisko		if (!sc->hw_crit_error) {
1307227068Sambrisko			/*
1308247369Ssmh			 * Initiate AEN (Asynchronous Event Notification) &
1309247369Ssmh			 * Sync Map
1310227068Sambrisko			 */
1311227068Sambrisko			mfi_aen_setup(sc, sc->last_seq_num);
1312247369Ssmh			mfi_tbolt_sync_map_info(sc);
1313247369Ssmh
1314227068Sambrisko			sc->issuepend_done = 1;
1315233711Sambrisko			device_printf(sc->mfi_dev, "second stage of reset "
1316227068Sambrisko			    "complete, FW is ready now.\n");
1317227068Sambrisko		} else {
1318233711Sambrisko			device_printf(sc->mfi_dev, "second stage of reset "
1319227068Sambrisko			     "never completed, hba was marked offline.\n");
1320227068Sambrisko		}
1321227068Sambrisko	} else {
1322227068Sambrisko		device_printf(sc->mfi_dev, "mfi_process_fw_state_chg_isr "
1323227068Sambrisko		    "called with unhandled value:%d\n", sc->adpreset);
1324227068Sambrisko	}
1325227068Sambrisko}
1326235014Sambrisko
1327235014Sambrisko/*
1328235014Sambrisko * The ThunderBolt HW has an option for the driver to directly
1329235016Sambrisko * access the underlying disks and operate on the RAID.  To
1330235014Sambrisko * do this there needs to be a capability to keep the RAID controller
1331235014Sambrisko * and driver in sync.  The FreeBSD driver does not take advantage
1332235014Sambrisko * of this feature since it adds a lot of complexity and slows down
1333235014Sambrisko * performance.  Performance is gained by using the controller's
1334235014Sambrisko * cache etc.
1335235014Sambrisko *
1336235014Sambrisko * Even though this driver doesn't access the disks directly, an
1337235014Sambrisko * AEN like command is used to inform the RAID firmware to "sync"
1338235014Sambrisko * with all LD's via the MFI_DCMD_LD_MAP_GET_INFO command.  This
1339235014Sambrisko * command in write mode will return when the RAID firmware has
1340235014Sambrisko * detected a change to the RAID state.  Examples of this type
1341235014Sambrisko * of change are removing a disk.  Once the command returns then
1342235014Sambrisko * the driver needs to acknowledge this and "sync" all LD's again.
1343235014Sambrisko * This repeats until we shutdown.  Then we need to cancel this
1344235014Sambrisko * pending command.
1345235014Sambrisko *
1346235014Sambrisko * If this is not done right the RAID firmware will not remove a
1347235014Sambrisko * pulled drive and the RAID won't go degraded etc.  Effectively,
1348235014Sambrisko * stopping any RAID mangement to functions.
1349235014Sambrisko *
1350235014Sambrisko * Doing another LD sync, requires the use of an event since the
1351235014Sambrisko * driver needs to do a mfi_wait_command and can't do that in an
1352235014Sambrisko * interrupt thread.
1353235014Sambrisko *
1354235014Sambrisko * The driver could get the RAID state via the MFI_DCMD_LD_MAP_GET_INFO
1355235014Sambrisko * That requires a bunch of structure and it is simplier to just do
1356235014Sambrisko * the MFI_DCMD_LD_GET_LIST versus walking the RAID map.
1357235014Sambrisko */
1358235014Sambrisko
1359235014Sambriskovoid
1360235014Sambriskomfi_tbolt_sync_map_info(struct mfi_softc *sc)
1361235014Sambrisko{
1362235014Sambrisko	int error = 0, i;
1363247369Ssmh	struct mfi_command *cmd = NULL;
1364247369Ssmh	struct mfi_dcmd_frame *dcmd = NULL;
1365235014Sambrisko	uint32_t context = 0;
1366247369Ssmh	union mfi_ld_ref *ld_sync = NULL;
1367235014Sambrisko	size_t ld_size;
1368235014Sambrisko	struct mfi_frame_header *hdr;
1369235014Sambrisko	struct mfi_command *cm = NULL;
1370235014Sambrisko	struct mfi_ld_list *list = NULL;
1371235014Sambrisko
1372247369Ssmh	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
1373247369Ssmh
1374235014Sambrisko	if (sc->mfi_map_sync_cm != NULL || sc->cm_map_abort)
1375235014Sambrisko		return;
1376235014Sambrisko
1377235014Sambrisko	error = mfi_dcmd_command(sc, &cm, MFI_DCMD_LD_GET_LIST,
1378235014Sambrisko	    (void **)&list, sizeof(*list));
1379235014Sambrisko	if (error)
1380235014Sambrisko		goto out;
1381235014Sambrisko
1382235014Sambrisko	cm->cm_flags = MFI_CMD_POLLED | MFI_CMD_DATAIN;
1383247369Ssmh
1384235014Sambrisko	if (mfi_wait_command(sc, cm) != 0) {
1385235014Sambrisko		device_printf(sc->mfi_dev, "Failed to get device listing\n");
1386235014Sambrisko		goto out;
1387235014Sambrisko	}
1388235014Sambrisko
1389235014Sambrisko	hdr = &cm->cm_frame->header;
1390235014Sambrisko	if (hdr->cmd_status != MFI_STAT_OK) {
1391235014Sambrisko		device_printf(sc->mfi_dev, "MFI_DCMD_LD_GET_LIST failed %x\n",
1392235014Sambrisko			      hdr->cmd_status);
1393235014Sambrisko		goto out;
1394235014Sambrisko	}
1395235014Sambrisko
1396235014Sambrisko	ld_size = sizeof(*ld_sync) * list->ld_count;
1397235014Sambrisko	ld_sync = (union mfi_ld_ref *) malloc(ld_size, M_MFIBUF,
1398247369Ssmh	     M_NOWAIT | M_ZERO);
1399235040Sambrisko	if (ld_sync == NULL) {
1400235040Sambrisko		device_printf(sc->mfi_dev, "Failed to allocate sync\n");
1401235040Sambrisko		goto out;
1402235040Sambrisko	}
1403247369Ssmh	for (i = 0; i < list->ld_count; i++)
1404235014Sambrisko		ld_sync[i].ref = list->ld_list[i].ld.ref;
1405235014Sambrisko
1406235040Sambrisko	if ((cmd = mfi_dequeue_free(sc)) == NULL) {
1407235040Sambrisko		device_printf(sc->mfi_dev, "Failed to get command\n");
1408235040Sambrisko		free(ld_sync, M_MFIBUF);
1409235040Sambrisko		goto out;
1410235040Sambrisko	}
1411242681Sambrisko
1412235014Sambrisko	context = cmd->cm_frame->header.context;
1413235014Sambrisko	bzero(cmd->cm_frame, sizeof(union mfi_frame));
1414235014Sambrisko	cmd->cm_frame->header.context = context;
1415235014Sambrisko
1416235014Sambrisko	dcmd = &cmd->cm_frame->dcmd;
1417235014Sambrisko	bzero(dcmd->mbox, MFI_MBOX_SIZE);
1418235014Sambrisko	dcmd->header.cmd = MFI_CMD_DCMD;
1419235014Sambrisko	dcmd->header.flags = MFI_FRAME_DIR_WRITE;
1420235014Sambrisko	dcmd->header.timeout = 0;
1421235014Sambrisko	dcmd->header.data_len = ld_size;
1422235014Sambrisko	dcmd->header.scsi_status = 0;
1423235014Sambrisko	dcmd->opcode = MFI_DCMD_LD_MAP_GET_INFO;
1424235014Sambrisko	cmd->cm_sg = &dcmd->sgl;
1425235014Sambrisko	cmd->cm_total_frame_size = MFI_DCMD_FRAME_SIZE;
1426235014Sambrisko	cmd->cm_data = ld_sync;
1427235014Sambrisko	cmd->cm_private = ld_sync;
1428235014Sambrisko
1429235014Sambrisko	cmd->cm_len = ld_size;
1430235014Sambrisko	cmd->cm_complete = mfi_sync_map_complete;
1431235014Sambrisko	sc->mfi_map_sync_cm = cmd;
1432235014Sambrisko
1433235014Sambrisko	cmd->cm_flags = MFI_CMD_DATAOUT;
1434235014Sambrisko	cmd->cm_frame->dcmd.mbox[0] = list->ld_count;
1435235014Sambrisko	cmd->cm_frame->dcmd.mbox[1] = MFI_DCMD_MBOX_PEND_FLAG;
1436235014Sambrisko
1437235014Sambrisko	if ((error = mfi_mapcmd(sc, cmd)) != 0) {
1438235014Sambrisko		device_printf(sc->mfi_dev, "failed to send map sync\n");
1439235040Sambrisko		free(ld_sync, M_MFIBUF);
1440235040Sambrisko		sc->mfi_map_sync_cm = NULL;
1441247369Ssmh		mfi_release_command(cmd);
1442235040Sambrisko		goto out;
1443235014Sambrisko	}
1444235014Sambrisko
1445235014Sambriskoout:
1446235014Sambrisko	if (list)
1447235014Sambrisko		free(list, M_MFIBUF);
1448235014Sambrisko	if (cm)
1449235014Sambrisko		mfi_release_command(cm);
1450235014Sambrisko}
1451235014Sambrisko
1452235014Sambriskostatic void
1453235014Sambriskomfi_sync_map_complete(struct mfi_command *cm)
1454235014Sambrisko{
1455235014Sambrisko	struct mfi_frame_header *hdr;
1456235014Sambrisko	struct mfi_softc *sc;
1457235014Sambrisko	int aborted = 0;
1458235014Sambrisko
1459235014Sambrisko	sc = cm->cm_sc;
1460235014Sambrisko	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
1461235014Sambrisko
1462235014Sambrisko	hdr = &cm->cm_frame->header;
1463235014Sambrisko
1464235014Sambrisko	if (sc->mfi_map_sync_cm == NULL)
1465235014Sambrisko		return;
1466235014Sambrisko
1467235014Sambrisko	if (sc->cm_map_abort ||
1468235014Sambrisko	    hdr->cmd_status == MFI_STAT_INVALID_STATUS) {
1469235014Sambrisko		sc->cm_map_abort = 0;
1470235014Sambrisko		aborted = 1;
1471235014Sambrisko	}
1472235014Sambrisko
1473235014Sambrisko	free(cm->cm_data, M_MFIBUF);
1474247369Ssmh	wakeup(&sc->mfi_map_sync_cm);
1475235014Sambrisko	sc->mfi_map_sync_cm = NULL;
1476235014Sambrisko	mfi_release_command(cm);
1477235014Sambrisko
1478235014Sambrisko	/* set it up again so the driver can catch more events */
1479247369Ssmh	if (!aborted)
1480235014Sambrisko		mfi_queue_map_sync(sc);
1481235014Sambrisko}
1482235014Sambrisko
1483235014Sambriskostatic void
1484235014Sambriskomfi_queue_map_sync(struct mfi_softc *sc)
1485235014Sambrisko{
1486235014Sambrisko	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
1487235014Sambrisko	taskqueue_enqueue(taskqueue_swi, &sc->mfi_map_sync_task);
1488235014Sambrisko}
1489235014Sambrisko
1490235014Sambriskovoid
1491235014Sambriskomfi_handle_map_sync(void *context, int pending)
1492235014Sambrisko{
1493235014Sambrisko	struct mfi_softc *sc;
1494235014Sambrisko
1495235014Sambrisko	sc = context;
1496247369Ssmh	mtx_lock(&sc->mfi_io_lock);
1497235014Sambrisko	mfi_tbolt_sync_map_info(sc);
1498247369Ssmh	mtx_unlock(&sc->mfi_io_lock);
1499235014Sambrisko}
1500