1331722Seadler /*-
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: stable/11/sys/dev/mfi/mfi_tbolt.c 360843 2020-05-09 11:18:34Z dim $");
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
89247369SsmhSYSCTL_INT(_hw_mfi, OID_AUTO, fw_reset_test, CTLFLAG_RWTUN, &mfi_fw_reset_test,
90247369Ssmh           0, "Force a firmware reset condition");
91247369Ssmh#endif
92247369Ssmh
93227068Sambriskovoid
94227068Sambriskomfi_tbolt_enable_intr_ppc(struct mfi_softc *sc)
95227068Sambrisko{
96227068Sambrisko	MFI_WRITE4(sc, MFI_OMSK, ~MFI_FUSION_ENABLE_INTERRUPT_MASK);
97227068Sambrisko	MFI_READ4(sc, MFI_OMSK);
98227068Sambrisko}
99227068Sambrisko
100227068Sambriskovoid
101227068Sambriskomfi_tbolt_disable_intr_ppc(struct mfi_softc *sc)
102227068Sambrisko{
103227068Sambrisko	MFI_WRITE4(sc, MFI_OMSK, 0xFFFFFFFF);
104227068Sambrisko	MFI_READ4(sc, MFI_OMSK);
105227068Sambrisko}
106227068Sambrisko
107227068Sambriskoint32_t
108227068Sambriskomfi_tbolt_read_fw_status_ppc(struct mfi_softc *sc)
109227068Sambrisko{
110227068Sambrisko	return MFI_READ4(sc, MFI_OSP0);
111227068Sambrisko}
112227068Sambrisko
113227068Sambriskoint32_t
114227068Sambriskomfi_tbolt_check_clear_intr_ppc(struct mfi_softc *sc)
115227068Sambrisko{
116227068Sambrisko	int32_t status, mfi_status = 0;
117227068Sambrisko
118227068Sambrisko	status = MFI_READ4(sc, MFI_OSTS);
119227068Sambrisko
120233711Sambrisko	if (status & 1) {
121227068Sambrisko		MFI_WRITE4(sc, MFI_OSTS, status);
122227068Sambrisko		MFI_READ4(sc, MFI_OSTS);
123233711Sambrisko		if (status & MFI_STATE_CHANGE_INTERRUPT) {
124227068Sambrisko			mfi_status |= MFI_FIRMWARE_STATE_CHANGE;
125227068Sambrisko		}
126227068Sambrisko
127227068Sambrisko		return mfi_status;
128227068Sambrisko	}
129233711Sambrisko	if (!(status & MFI_FUSION_ENABLE_INTERRUPT_MASK))
130227068Sambrisko		return 1;
131227068Sambrisko
132227068Sambrisko	MFI_READ4(sc, MFI_OSTS);
133227068Sambrisko	return 0;
134227068Sambrisko}
135227068Sambrisko
136227068Sambrisko
137227068Sambriskovoid
138233711Sambriskomfi_tbolt_issue_cmd_ppc(struct mfi_softc *sc, bus_addr_t bus_add,
139227068Sambrisko   uint32_t frame_cnt)
140227068Sambrisko{
141227068Sambrisko	bus_add |= (MFI_REQ_DESCRIPT_FLAGS_MFA
142227068Sambrisko	    << MFI_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
143233711Sambrisko	MFI_WRITE4(sc, MFI_IQPL, (uint32_t)bus_add);
144233711Sambrisko	MFI_WRITE4(sc, MFI_IQPH, (uint32_t)((uint64_t)bus_add >> 32));
145227068Sambrisko}
146227068Sambrisko
147235016Sambrisko/*
148227068Sambrisko * mfi_tbolt_adp_reset - For controller reset
149227068Sambrisko * @regs: MFI register set
150227068Sambrisko */
151235016Sambriskoint
152235016Sambriskomfi_tbolt_adp_reset(struct mfi_softc *sc)
153227068Sambrisko{
154233711Sambrisko	int retry = 0, i = 0;
155233711Sambrisko	int HostDiag;
156227068Sambrisko
157233711Sambrisko	MFI_WRITE4(sc, MFI_WSR, 0xF);
158233711Sambrisko	MFI_WRITE4(sc, MFI_WSR, 4);
159233711Sambrisko	MFI_WRITE4(sc, MFI_WSR, 0xB);
160233711Sambrisko	MFI_WRITE4(sc, MFI_WSR, 2);
161233711Sambrisko	MFI_WRITE4(sc, MFI_WSR, 7);
162233711Sambrisko	MFI_WRITE4(sc, MFI_WSR, 0xD);
163227068Sambrisko
164233711Sambrisko	for (i = 0; i < 10000; i++) ;
165227068Sambrisko
166233711Sambrisko	HostDiag = (uint32_t)MFI_READ4(sc, MFI_HDR);
167227068Sambrisko
168233711Sambrisko	while (!( HostDiag & DIAG_WRITE_ENABLE)) {
169233711Sambrisko		for (i = 0; i < 1000; i++);
170233711Sambrisko		HostDiag = (uint32_t)MFI_READ4(sc, MFI_HDR);
171247369Ssmh		device_printf(sc->mfi_dev, "ADP_RESET_TBOLT: retry time=%d, "
172247369Ssmh		    "hostdiag=%#x\n", retry, HostDiag);
173227068Sambrisko
174233711Sambrisko		if (retry++ >= 100)
175233711Sambrisko			return 1;
176233711Sambrisko	}
177227068Sambrisko
178247369Ssmh	device_printf(sc->mfi_dev, "ADP_RESET_TBOLT: HostDiag=%#x\n", HostDiag);
179227068Sambrisko
180233711Sambrisko	MFI_WRITE4(sc, MFI_HDR, (HostDiag | DIAG_RESET_ADAPTER));
181227068Sambrisko
182233711Sambrisko	for (i=0; i < 10; i++) {
183233711Sambrisko		for (i = 0; i < 10000; i++);
184233711Sambrisko	}
185227068Sambrisko
186233711Sambrisko	HostDiag = (uint32_t)MFI_READ4(sc, MFI_RSR);
187233711Sambrisko	while (HostDiag & DIAG_RESET_ADAPTER) {
188233711Sambrisko		for (i = 0; i < 1000; i++) ;
189233711Sambrisko		HostDiag = (uint32_t)MFI_READ4(sc, MFI_RSR);
190247369Ssmh		device_printf(sc->mfi_dev, "ADP_RESET_TBOLT: retry time=%d, "
191247369Ssmh		    "hostdiag=%#x\n", retry, HostDiag);
192227068Sambrisko
193233711Sambrisko		if (retry++ >= 1000)
194233711Sambrisko			return 1;
195233711Sambrisko	}
196233711Sambrisko	return 0;
197227068Sambrisko}
198227068Sambrisko
199227068Sambrisko/*
200235016Sambrisko * This routine initialize Thunderbolt specific device information
201227068Sambrisko */
202235016Sambriskovoid
203235016Sambriskomfi_tbolt_init_globals(struct mfi_softc *sc)
204227068Sambrisko{
205227068Sambrisko	/* Initialize single reply size and Message size */
206227068Sambrisko	sc->reply_size = MEGASAS_THUNDERBOLT_REPLY_SIZE;
207227068Sambrisko	sc->raid_io_msg_size = MEGASAS_THUNDERBOLT_NEW_MSG_SIZE;
208227068Sambrisko
209227068Sambrisko	/*
210227068Sambrisko	 * Calculating how many SGEs allowed in a allocated main message
211227068Sambrisko	 * (size of the Message - Raid SCSI IO message size(except SGE))
212227068Sambrisko	 * / size of SGE
213227068Sambrisko	 * (0x100 - (0x90 - 0x10)) / 0x10 = 8
214227068Sambrisko	 */
215227068Sambrisko	sc->max_SGEs_in_main_message =
216227068Sambrisko	    (uint8_t)((sc->raid_io_msg_size
217227068Sambrisko	    - (sizeof(struct mfi_mpi2_request_raid_scsi_io)
218227068Sambrisko	    - sizeof(MPI2_SGE_IO_UNION))) / sizeof(MPI2_SGE_IO_UNION));
219227068Sambrisko	/*
220227068Sambrisko	 * (Command frame size allocaed in SRB ext - Raid SCSI IO message size)
221227068Sambrisko	 * / size of SGL ;
222227068Sambrisko	 * (1280 - 256) / 16 = 64
223227068Sambrisko	 */
224227068Sambrisko	sc->max_SGEs_in_chain_message = (MR_COMMAND_SIZE
225227068Sambrisko	    - sc->raid_io_msg_size) / sizeof(MPI2_SGE_IO_UNION);
226227068Sambrisko	/*
227227068Sambrisko	 * (0x08-1) + 0x40 = 0x47 - 0x01 = 0x46  one is left for command
228227068Sambrisko	 * colscing
229227068Sambrisko	*/
230227068Sambrisko	sc->mfi_max_sge = (sc->max_SGEs_in_main_message - 1)
231227068Sambrisko	    + sc->max_SGEs_in_chain_message - 1;
232227068Sambrisko	/*
233227068Sambrisko	* This is the offset in number of 4 * 32bit words to the next chain
234227068Sambrisko	* (0x100 - 0x10)/0x10 = 0xF(15)
235227068Sambrisko	*/
236227068Sambrisko	sc->chain_offset_value_for_main_message = (sc->raid_io_msg_size
237227068Sambrisko	    - sizeof(MPI2_SGE_IO_UNION))/16;
238227068Sambrisko	sc->chain_offset_value_for_mpt_ptmsg
239227068Sambrisko	    = offsetof(struct mfi_mpi2_request_raid_scsi_io, SGL)/16;
240227068Sambrisko	sc->mfi_cmd_pool_tbolt = NULL;
241227068Sambrisko	sc->request_desc_pool = NULL;
242227068Sambrisko}
243227068Sambrisko
244227068Sambrisko/*
245235016Sambrisko * This function calculates the memory requirement for Thunderbolt
246235016Sambrisko * controller, returns the total required memory in bytes
247227068Sambrisko */
248227068Sambrisko
249235016Sambriskouint32_t
250235016Sambriskomfi_tbolt_get_memory_requirement(struct mfi_softc *sc)
251227068Sambrisko{
252227068Sambrisko	uint32_t size;
253233711Sambrisko	size = MEGASAS_THUNDERBOLT_MSG_ALLIGNMENT;	/* for Alignment */
254227068Sambrisko	size += sc->raid_io_msg_size * (sc->mfi_max_fw_cmds + 1);
255227068Sambrisko	size += sc->reply_size * sc->mfi_max_fw_cmds;
256233711Sambrisko	/* this is for SGL's */
257227068Sambrisko	size += MEGASAS_MAX_SZ_CHAIN_FRAME * sc->mfi_max_fw_cmds;
258227068Sambrisko	return size;
259227068Sambrisko}
260227068Sambrisko
261227068Sambrisko/*
262227068Sambrisko * Description:
263227068Sambrisko *      This function will prepare message pools for the Thunderbolt controller
264227068Sambrisko * Arguments:
265227068Sambrisko *      DevExt - HBA miniport driver's adapter data storage structure
266227068Sambrisko *      pMemLocation - start of the memory allocated for Thunderbolt.
267227068Sambrisko * Return Value:
268227068Sambrisko *      TRUE if successful
269227068Sambrisko *      FALSE if failed
270227068Sambrisko */
271235016Sambriskoint
272235016Sambriskomfi_tbolt_init_desc_pool(struct mfi_softc *sc, uint8_t* mem_location,
273227068Sambrisko    uint32_t tbolt_contg_length)
274227068Sambrisko{
275227068Sambrisko	uint32_t     offset = 0;
276227068Sambrisko	uint8_t      *addr = mem_location;
277227068Sambrisko
278227068Sambrisko	/* Request Descriptor Base physical Address */
279227068Sambrisko
280227068Sambrisko	/* For Request Decriptors Virtual Memory */
281227068Sambrisko	/* Initialise the aligned IO Frames Virtual Memory Pointer */
282233711Sambrisko	if (((uintptr_t)addr) & (0xFF)) {
283227068Sambrisko		addr = &addr[sc->raid_io_msg_size];
284227068Sambrisko		addr = (uint8_t *)((uintptr_t)addr & (~0xFF));
285227068Sambrisko		sc->request_message_pool_align = addr;
286227068Sambrisko	} else
287227068Sambrisko		sc->request_message_pool_align = addr;
288227068Sambrisko
289227068Sambrisko	offset = sc->request_message_pool_align - sc->request_message_pool;
290227068Sambrisko	sc->request_msg_busaddr = sc->mfi_tb_busaddr + offset;
291227068Sambrisko
292227068Sambrisko	/* DJA XXX should this be bus dma ??? */
293227068Sambrisko	/* Skip request message pool */
294227068Sambrisko	addr = &addr[sc->raid_io_msg_size * (sc->mfi_max_fw_cmds + 1)];
295227068Sambrisko	/* Reply Frame Pool is initialized */
296227068Sambrisko	sc->reply_frame_pool = (struct mfi_mpi2_reply_header *) addr;
297233711Sambrisko	if (((uintptr_t)addr) & (0xFF)) {
298227068Sambrisko		addr = &addr[sc->reply_size];
299227068Sambrisko		addr = (uint8_t *)((uintptr_t)addr & (~0xFF));
300227068Sambrisko	}
301227068Sambrisko	sc->reply_frame_pool_align
302227068Sambrisko		    = (struct mfi_mpi2_reply_header *)addr;
303227068Sambrisko
304227068Sambrisko	offset = (uintptr_t)sc->reply_frame_pool_align
305227068Sambrisko	    - (uintptr_t)sc->request_message_pool;
306227068Sambrisko	sc->reply_frame_busaddr = sc->mfi_tb_busaddr + offset;
307227068Sambrisko
308227068Sambrisko	/* Skip Reply Frame Pool */
309227068Sambrisko	addr += sc->reply_size * sc->mfi_max_fw_cmds;
310227068Sambrisko	sc->reply_pool_limit = addr;
311227068Sambrisko
312227068Sambrisko	/* initializing reply address to 0xFFFFFFFF */
313227068Sambrisko	memset((uint8_t *)sc->reply_frame_pool, 0xFF,
314227068Sambrisko	       (sc->reply_size * sc->mfi_max_fw_cmds));
315227068Sambrisko
316227068Sambrisko	offset = sc->reply_size * sc->mfi_max_fw_cmds;
317227068Sambrisko	sc->sg_frame_busaddr = sc->reply_frame_busaddr + offset;
318227068Sambrisko	/* initialize the last_reply_idx to 0 */
319227068Sambrisko	sc->last_reply_idx = 0;
320247369Ssmh	MFI_WRITE4(sc, MFI_RFPI, sc->mfi_max_fw_cmds - 1);
321247369Ssmh	MFI_WRITE4(sc, MFI_RPI, sc->last_reply_idx);
322227068Sambrisko	offset = (sc->sg_frame_busaddr + (MEGASAS_MAX_SZ_CHAIN_FRAME *
323227068Sambrisko	    sc->mfi_max_fw_cmds)) - sc->mfi_tb_busaddr;
324233711Sambrisko	if (offset > tbolt_contg_length)
325233711Sambrisko		device_printf(sc->mfi_dev, "Error:Initialized more than "
326227068Sambrisko		    "allocated\n");
327227068Sambrisko	return 0;
328227068Sambrisko}
329227068Sambrisko
330227068Sambrisko/*
331235016Sambrisko * This routine prepare and issue INIT2 frame to the Firmware
332227068Sambrisko */
333227068Sambrisko
334227068Sambriskoint
335227068Sambriskomfi_tbolt_init_MFI_queue(struct mfi_softc *sc)
336227068Sambrisko{
337227068Sambrisko	struct MPI2_IOC_INIT_REQUEST   *mpi2IocInit;
338247369Ssmh	struct mfi_init_frame		*mfi_init;
339227068Sambrisko	uintptr_t			offset = 0;
340233711Sambrisko	bus_addr_t			phyAddress;
341227068Sambrisko	MFI_ADDRESS			*mfiAddressTemp;
342247369Ssmh	struct mfi_command		*cm, cmd_tmp;
343227068Sambrisko	int error;
344227068Sambrisko
345247369Ssmh	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
346247369Ssmh
347227068Sambrisko	/* Check if initialization is already completed */
348233711Sambrisko	if (sc->MFA_enabled) {
349247369Ssmh		device_printf(sc->mfi_dev, "tbolt_init already initialised!\n");
350227068Sambrisko		return 1;
351227068Sambrisko	}
352227068Sambrisko
353227068Sambrisko	if ((cm = mfi_dequeue_free(sc)) == NULL) {
354247369Ssmh		device_printf(sc->mfi_dev, "tbolt_init failed to get command "
355247369Ssmh		    " entry!\n");
356227068Sambrisko		return (EBUSY);
357227068Sambrisko	}
358247369Ssmh
359247369Ssmh	cmd_tmp.cm_frame = cm->cm_frame;
360247369Ssmh	cmd_tmp.cm_frame_busaddr = cm->cm_frame_busaddr;
361247369Ssmh	cmd_tmp.cm_dmamap = cm->cm_dmamap;
362247369Ssmh
363227068Sambrisko	cm->cm_frame = (union mfi_frame *)((uintptr_t)sc->mfi_tb_init);
364227068Sambrisko	cm->cm_frame_busaddr = sc->mfi_tb_init_busaddr;
365227068Sambrisko	cm->cm_dmamap = sc->mfi_tb_init_dmamap;
366227068Sambrisko	cm->cm_frame->header.context = 0;
367227068Sambrisko
368227068Sambrisko	/*
369227068Sambrisko	 * Abuse the SG list area of the frame to hold the init_qinfo
370227068Sambrisko	 * object;
371227068Sambrisko	 */
372227068Sambrisko	mfi_init = &cm->cm_frame->init;
373227068Sambrisko
374247369Ssmh	mpi2IocInit = (struct MPI2_IOC_INIT_REQUEST *)sc->mfi_tb_ioc_init_desc;
375227068Sambrisko	bzero(mpi2IocInit, sizeof(struct MPI2_IOC_INIT_REQUEST));
376227068Sambrisko	mpi2IocInit->Function  = MPI2_FUNCTION_IOC_INIT;
377227068Sambrisko	mpi2IocInit->WhoInit   = MPI2_WHOINIT_HOST_DRIVER;
378227068Sambrisko
379227068Sambrisko	/* set MsgVersion and HeaderVersion host driver was built with */
380227068Sambrisko	mpi2IocInit->MsgVersion = MPI2_VERSION;
381227068Sambrisko	mpi2IocInit->HeaderVersion = MPI2_HEADER_VERSION;
382227068Sambrisko	mpi2IocInit->SystemRequestFrameSize = sc->raid_io_msg_size/4;
383227068Sambrisko	mpi2IocInit->ReplyDescriptorPostQueueDepth
384227068Sambrisko	    = (uint16_t)sc->mfi_max_fw_cmds;
385227068Sambrisko	mpi2IocInit->ReplyFreeQueueDepth = 0; /* Not supported by MR. */
386227068Sambrisko
387227068Sambrisko	/* Get physical address of reply frame pool */
388227068Sambrisko	offset = (uintptr_t) sc->reply_frame_pool_align
389227068Sambrisko	    - (uintptr_t)sc->request_message_pool;
390227068Sambrisko	phyAddress = sc->mfi_tb_busaddr + offset;
391227068Sambrisko	mfiAddressTemp =
392227068Sambrisko	    (MFI_ADDRESS *)&mpi2IocInit->ReplyDescriptorPostQueueAddress;
393233711Sambrisko	mfiAddressTemp->u.addressLow = (uint32_t)phyAddress;
394233711Sambrisko	mfiAddressTemp->u.addressHigh = (uint32_t)((uint64_t)phyAddress >> 32);
395227068Sambrisko
396227068Sambrisko	/* Get physical address of request message pool */
397227068Sambrisko	offset = sc->request_message_pool_align - sc->request_message_pool;
398227068Sambrisko	phyAddress =  sc->mfi_tb_busaddr + offset;
399227068Sambrisko	mfiAddressTemp = (MFI_ADDRESS *)&mpi2IocInit->SystemRequestFrameBaseAddress;
400233711Sambrisko	mfiAddressTemp->u.addressLow = (uint32_t)phyAddress;
401233711Sambrisko	mfiAddressTemp->u.addressHigh = (uint32_t)((uint64_t)phyAddress >> 32);
402233711Sambrisko	mpi2IocInit->ReplyFreeQueueAddress =  0; /* Not supported by MR. */
403227068Sambrisko	mpi2IocInit->TimeStamp = time_uptime;
404227068Sambrisko
405227068Sambrisko	if (sc->verbuf) {
406227068Sambrisko		snprintf((char *)sc->verbuf, strlen(MEGASAS_VERSION) + 2, "%s\n",
407227068Sambrisko                MEGASAS_VERSION);
408233711Sambrisko		mfi_init->driver_ver_lo = (uint32_t)sc->verbuf_h_busaddr;
409233711Sambrisko		mfi_init->driver_ver_hi =
410233711Sambrisko		    (uint32_t)((uint64_t)sc->verbuf_h_busaddr >> 32);
411227068Sambrisko	}
412227068Sambrisko	/* Get the physical address of the mpi2 ioc init command */
413227068Sambrisko	phyAddress =  sc->mfi_tb_ioc_init_busaddr;
414233711Sambrisko	mfi_init->qinfo_new_addr_lo = (uint32_t)phyAddress;
415233711Sambrisko	mfi_init->qinfo_new_addr_hi = (uint32_t)((uint64_t)phyAddress >> 32);
416227068Sambrisko	mfi_init->header.flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE;
417227068Sambrisko
418227068Sambrisko	mfi_init->header.cmd = MFI_CMD_INIT;
419227068Sambrisko	mfi_init->header.data_len = sizeof(struct MPI2_IOC_INIT_REQUEST);
420227068Sambrisko	mfi_init->header.cmd_status = MFI_STAT_INVALID_STATUS;
421227068Sambrisko
422227068Sambrisko	cm->cm_data = NULL;
423227068Sambrisko	cm->cm_flags |= MFI_CMD_POLLED;
424227068Sambrisko	cm->cm_timestamp = time_uptime;
425227068Sambrisko	if ((error = mfi_mapcmd(sc, cm)) != 0) {
426227068Sambrisko		device_printf(sc->mfi_dev, "failed to send IOC init2 "
427227068Sambrisko		    "command %d at %lx\n", error, (long)cm->cm_frame_busaddr);
428247369Ssmh		goto out;
429227068Sambrisko	}
430227068Sambrisko
431247369Ssmh	if (mfi_init->header.cmd_status == MFI_STAT_OK) {
432227068Sambrisko		sc->MFA_enabled = 1;
433247369Ssmh	} else {
434247369Ssmh		device_printf(sc->mfi_dev, "Init command Failed %#x\n",
435227068Sambrisko		    mfi_init->header.cmd_status);
436247369Ssmh		error = mfi_init->header.cmd_status;
437247369Ssmh		goto out;
438227068Sambrisko	}
439227068Sambrisko
440247369Ssmhout:
441247369Ssmh	cm->cm_frame = cmd_tmp.cm_frame;
442247369Ssmh	cm->cm_frame_busaddr = cmd_tmp.cm_frame_busaddr;
443247369Ssmh	cm->cm_dmamap = cmd_tmp.cm_dmamap;
444247369Ssmh	mfi_release_command(cm);
445227068Sambrisko
446247369Ssmh	return (error);
447247369Ssmh
448227068Sambrisko}
449227068Sambrisko
450235016Sambriskoint
451235016Sambriskomfi_tbolt_alloc_cmd(struct mfi_softc *sc)
452227068Sambrisko{
453227068Sambrisko	struct mfi_cmd_tbolt *cmd;
454233711Sambrisko	bus_addr_t io_req_base_phys;
455227068Sambrisko	uint8_t *io_req_base;
456233711Sambrisko	int i = 0, j = 0, offset = 0;
457227068Sambrisko
458227068Sambrisko	/*
459227068Sambrisko	 * sc->mfi_cmd_pool_tbolt is an array of struct mfi_cmd_tbolt pointers.
460227068Sambrisko	 * Allocate the dynamic array first and then allocate individual
461227068Sambrisko	 * commands.
462227068Sambrisko	 */
463227068Sambrisko	sc->request_desc_pool = malloc(sizeof(
464227068Sambrisko	    union mfi_mpi2_request_descriptor) * sc->mfi_max_fw_cmds,
465227068Sambrisko	    M_MFIBUF, M_NOWAIT|M_ZERO);
466247369Ssmh
467247369Ssmh	if (sc->request_desc_pool == NULL) {
468247369Ssmh		device_printf(sc->mfi_dev, "Could not alloc "
469247369Ssmh		    "memory for request_desc_pool\n");
470247369Ssmh		return (ENOMEM);
471247369Ssmh	}
472247369Ssmh
473227068Sambrisko	sc->mfi_cmd_pool_tbolt = malloc(sizeof(struct mfi_cmd_tbolt*)
474227068Sambrisko	    * sc->mfi_max_fw_cmds, M_MFIBUF, M_NOWAIT|M_ZERO);
475227068Sambrisko
476247369Ssmh	if (sc->mfi_cmd_pool_tbolt == NULL) {
477247369Ssmh		free(sc->request_desc_pool, M_MFIBUF);
478247369Ssmh		device_printf(sc->mfi_dev, "Could not alloc "
479247369Ssmh		    "memory for cmd_pool_tbolt\n");
480247369Ssmh		return (ENOMEM);
481227068Sambrisko	}
482227068Sambrisko
483227068Sambrisko	for (i = 0; i < sc->mfi_max_fw_cmds; i++) {
484227068Sambrisko		sc->mfi_cmd_pool_tbolt[i] = malloc(sizeof(
485227068Sambrisko		    struct mfi_cmd_tbolt),M_MFIBUF, M_NOWAIT|M_ZERO);
486227068Sambrisko
487227068Sambrisko		if (!sc->mfi_cmd_pool_tbolt[i]) {
488247369Ssmh			device_printf(sc->mfi_dev, "Could not alloc "
489247369Ssmh			    "cmd_pool_tbolt entry\n");
490227068Sambrisko
491227068Sambrisko			for (j = 0; j < i; j++)
492227068Sambrisko				free(sc->mfi_cmd_pool_tbolt[j], M_MFIBUF);
493227068Sambrisko
494247369Ssmh			free(sc->request_desc_pool, M_MFIBUF);
495247369Ssmh			sc->request_desc_pool = NULL;
496227068Sambrisko			free(sc->mfi_cmd_pool_tbolt, M_MFIBUF);
497227068Sambrisko			sc->mfi_cmd_pool_tbolt = NULL;
498247369Ssmh
499247369Ssmh			return (ENOMEM);
500227068Sambrisko		}
501227068Sambrisko	}
502227068Sambrisko
503227068Sambrisko	/*
504227068Sambrisko	 * The first 256 bytes (SMID 0) is not used. Don't add to the cmd
505247369Ssmh	 * list
506227068Sambrisko	 */
507227068Sambrisko	io_req_base = sc->request_message_pool_align
508227068Sambrisko		+ MEGASAS_THUNDERBOLT_NEW_MSG_SIZE;
509227068Sambrisko	io_req_base_phys = sc->request_msg_busaddr
510227068Sambrisko		+ MEGASAS_THUNDERBOLT_NEW_MSG_SIZE;
511227068Sambrisko
512227068Sambrisko	/*
513227068Sambrisko	 * Add all the commands to command pool (instance->cmd_pool)
514227068Sambrisko	 */
515227068Sambrisko	/* SMID 0 is reserved. Set SMID/index from 1 */
516227068Sambrisko
517227068Sambrisko	for (i = 0; i < sc->mfi_max_fw_cmds; i++) {
518227068Sambrisko		cmd = sc->mfi_cmd_pool_tbolt[i];
519227068Sambrisko		offset = MEGASAS_THUNDERBOLT_NEW_MSG_SIZE * i;
520227068Sambrisko		cmd->index = i + 1;
521227068Sambrisko		cmd->request_desc = (union mfi_mpi2_request_descriptor *)
522227068Sambrisko		    (sc->request_desc_pool + i);
523227068Sambrisko		cmd->io_request = (struct mfi_mpi2_request_raid_scsi_io *)
524227068Sambrisko		    (io_req_base + offset);
525227068Sambrisko		cmd->io_request_phys_addr = io_req_base_phys + offset;
526227068Sambrisko		cmd->sg_frame = (MPI2_SGE_IO_UNION *)(sc->reply_pool_limit
527227068Sambrisko		    + i * MEGASAS_MAX_SZ_CHAIN_FRAME);
528227068Sambrisko		cmd->sg_frame_phys_addr = sc->sg_frame_busaddr + i
529227068Sambrisko		    * MEGASAS_MAX_SZ_CHAIN_FRAME;
530242681Sambrisko		cmd->sync_cmd_idx = sc->mfi_max_fw_cmds;
531227068Sambrisko
532227068Sambrisko		TAILQ_INSERT_TAIL(&(sc->mfi_cmd_tbolt_tqh), cmd, next);
533227068Sambrisko	}
534227068Sambrisko	return 0;
535227068Sambrisko}
536227068Sambrisko
537235016Sambriskoint
538235016Sambriskomfi_tbolt_reset(struct mfi_softc *sc)
539227068Sambrisko{
540227068Sambrisko	uint32_t fw_state;
541227068Sambrisko
542227068Sambrisko	mtx_lock(&sc->mfi_io_lock);
543233711Sambrisko	if (sc->hw_crit_error) {
544233711Sambrisko		device_printf(sc->mfi_dev, "HW CRITICAL ERROR\n");
545227068Sambrisko		mtx_unlock(&sc->mfi_io_lock);
546227068Sambrisko		return 1;
547227068Sambrisko	}
548227068Sambrisko
549233711Sambrisko	if (sc->mfi_flags & MFI_FLAGS_TBOLT) {
550227068Sambrisko		fw_state = sc->mfi_read_fw_status(sc);
551247369Ssmh		if ((fw_state & MFI_FWSTATE_FAULT) == MFI_FWSTATE_FAULT ||
552247369Ssmh		    mfi_fw_reset_test) {
553233711Sambrisko			if ((sc->disableOnlineCtrlReset == 0)
554233711Sambrisko			    && (sc->adpreset == 0)) {
555233711Sambrisko				device_printf(sc->mfi_dev, "Adapter RESET "
556227068Sambrisko				    "condition is detected\n");
557227068Sambrisko				sc->adpreset = 1;
558227068Sambrisko				sc->issuepend_done = 0;
559227068Sambrisko				sc->MFA_enabled = 0;
560227068Sambrisko				sc->last_reply_idx = 0;
561227068Sambrisko				mfi_process_fw_state_chg_isr((void *) sc);
562227068Sambrisko			}
563227068Sambrisko			mtx_unlock(&sc->mfi_io_lock);
564227068Sambrisko			return 0;
565227068Sambrisko		}
566227068Sambrisko	}
567227068Sambrisko	mtx_unlock(&sc->mfi_io_lock);
568227068Sambrisko	return 1;
569227068Sambrisko}
570227068Sambrisko
571227068Sambrisko/*
572227068Sambrisko * mfi_intr_tbolt - isr entry point
573227068Sambrisko */
574235016Sambriskovoid
575235016Sambriskomfi_intr_tbolt(void *arg)
576227068Sambrisko{
577227068Sambrisko	struct mfi_softc *sc = (struct mfi_softc *)arg;
578227068Sambrisko
579233711Sambrisko	if (sc->mfi_check_clear_intr(sc) == 1) {
580227068Sambrisko		return;
581227068Sambrisko	}
582233711Sambrisko	if (sc->mfi_detaching)
583227068Sambrisko		return;
584227068Sambrisko	mtx_lock(&sc->mfi_io_lock);
585227068Sambrisko	mfi_tbolt_complete_cmd(sc);
586247369Ssmh	sc->mfi_flags &= ~MFI_FLAGS_QFRZN;
587227068Sambrisko	mfi_startio(sc);
588227068Sambrisko	mtx_unlock(&sc->mfi_io_lock);
589227068Sambrisko	return;
590227068Sambrisko}
591227068Sambrisko
592235016Sambrisko/*
593227068Sambrisko * map_cmd_status -	Maps FW cmd status to OS cmd status
594227068Sambrisko * @cmd :		Pointer to cmd
595227068Sambrisko * @status :		status of cmd returned by FW
596227068Sambrisko * @ext_status :	ext status of cmd returned by FW
597227068Sambrisko */
598227068Sambrisko
599227068Sambriskovoid
600227068Sambriskomap_tbolt_cmd_status(struct mfi_command *mfi_cmd, uint8_t status,
601227068Sambrisko    uint8_t ext_status)
602227068Sambrisko{
603227068Sambrisko	switch (status) {
604247369Ssmh	case MFI_STAT_OK:
605247369Ssmh		mfi_cmd->cm_frame->header.cmd_status = MFI_STAT_OK;
606247369Ssmh		mfi_cmd->cm_frame->dcmd.header.cmd_status = MFI_STAT_OK;
607247369Ssmh		mfi_cmd->cm_error = MFI_STAT_OK;
608247369Ssmh		break;
609227068Sambrisko
610247369Ssmh	case MFI_STAT_SCSI_IO_FAILED:
611247369Ssmh	case MFI_STAT_LD_INIT_IN_PROGRESS:
612247369Ssmh		mfi_cmd->cm_frame->header.cmd_status = status;
613247369Ssmh		mfi_cmd->cm_frame->header.scsi_status = ext_status;
614247369Ssmh		mfi_cmd->cm_frame->dcmd.header.cmd_status = status;
615247369Ssmh		mfi_cmd->cm_frame->dcmd.header.scsi_status
616247369Ssmh		    = ext_status;
617247369Ssmh		break;
618227068Sambrisko
619247369Ssmh	case MFI_STAT_SCSI_DONE_WITH_ERROR:
620247369Ssmh		mfi_cmd->cm_frame->header.cmd_status = ext_status;
621247369Ssmh		mfi_cmd->cm_frame->dcmd.header.cmd_status = ext_status;
622247369Ssmh		break;
623227068Sambrisko
624247369Ssmh	case MFI_STAT_LD_OFFLINE:
625247369Ssmh	case MFI_STAT_DEVICE_NOT_FOUND:
626247369Ssmh		mfi_cmd->cm_frame->header.cmd_status = status;
627247369Ssmh		mfi_cmd->cm_frame->dcmd.header.cmd_status = status;
628247369Ssmh		break;
629227068Sambrisko
630247369Ssmh	default:
631247369Ssmh		mfi_cmd->cm_frame->header.cmd_status = status;
632247369Ssmh		mfi_cmd->cm_frame->dcmd.header.cmd_status = status;
633247369Ssmh		break;
634247369Ssmh	}
635227068Sambrisko}
636227068Sambrisko
637235016Sambrisko/*
638233711Sambrisko * mfi_tbolt_return_cmd -	Return a cmd to free command pool
639233711Sambrisko * @instance:		Adapter soft state
640247369Ssmh * @tbolt_cmd:		Tbolt command packet to be returned to free command pool
641247369Ssmh * @mfi_cmd:		Oning MFI command packe
642233711Sambrisko */
643247369Ssmhvoid
644247369Ssmhmfi_tbolt_return_cmd(struct mfi_softc *sc, struct mfi_cmd_tbolt *tbolt_cmd,
645247369Ssmh    struct mfi_command *mfi_cmd)
646233711Sambrisko{
647233711Sambrisko	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
648227068Sambrisko
649247369Ssmh	mfi_cmd->cm_flags &= ~MFI_CMD_TBOLT;
650247369Ssmh	mfi_cmd->cm_extra_frames = 0;
651247369Ssmh	tbolt_cmd->sync_cmd_idx = sc->mfi_max_fw_cmds;
652247369Ssmh
653247369Ssmh	TAILQ_INSERT_TAIL(&sc->mfi_cmd_tbolt_tqh, tbolt_cmd, next);
654233711Sambrisko}
655227068Sambrisko
656235014Sambriskovoid
657235014Sambriskomfi_tbolt_complete_cmd(struct mfi_softc *sc)
658227068Sambrisko{
659227068Sambrisko	struct mfi_mpi2_reply_header *desc, *reply_desc;
660247369Ssmh	struct mfi_command *cmd_mfi;	/* For MFA Cmds */
661227068Sambrisko	struct mfi_cmd_tbolt *cmd_tbolt;
662227068Sambrisko	uint16_t smid;
663227068Sambrisko	uint8_t reply_descript_type;
664227068Sambrisko	struct mfi_mpi2_request_raid_scsi_io  *scsi_io_req;
665227068Sambrisko	uint32_t status, extStatus;
666227068Sambrisko	uint16_t num_completed;
667227068Sambrisko	union desc_value val;
668247369Ssmh	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
669227068Sambrisko
670227068Sambrisko	desc = (struct mfi_mpi2_reply_header *)
671227068Sambrisko		((uintptr_t)sc->reply_frame_pool_align
672227068Sambrisko		+ sc->last_reply_idx * sc->reply_size);
673227068Sambrisko	reply_desc = desc;
674227068Sambrisko
675247369Ssmh	if (reply_desc == NULL) {
676227068Sambrisko		device_printf(sc->mfi_dev, "reply desc is NULL!!\n");
677247369Ssmh		return;
678247369Ssmh	}
679227068Sambrisko
680227068Sambrisko	reply_descript_type = reply_desc->ReplyFlags
681227068Sambrisko	     & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
682227068Sambrisko	if (reply_descript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
683227068Sambrisko		return;
684227068Sambrisko
685227068Sambrisko	num_completed = 0;
686227068Sambrisko	val.word = ((union mfi_mpi2_reply_descriptor *)desc)->words;
687227068Sambrisko
688227068Sambrisko	/* Read Reply descriptor */
689227068Sambrisko	while ((val.u.low != 0xFFFFFFFF) && (val.u.high != 0xFFFFFFFF)) {
690227068Sambrisko		smid = reply_desc->SMID;
691247369Ssmh		if (smid == 0 || smid > sc->mfi_max_fw_cmds) {
692247369Ssmh			device_printf(sc->mfi_dev, "smid is %d cannot "
693247369Ssmh			    "proceed - skipping\n", smid);
694247369Ssmh			goto next;
695227068Sambrisko		}
696227068Sambrisko		cmd_tbolt = sc->mfi_cmd_pool_tbolt[smid - 1];
697247369Ssmh		if (cmd_tbolt->sync_cmd_idx == sc->mfi_max_fw_cmds) {
698247369Ssmh			device_printf(sc->mfi_dev, "cmd_tbolt %p "
699247369Ssmh			    "has invalid sync_cmd_idx=%d - skipping\n",
700247369Ssmh			    cmd_tbolt, cmd_tbolt->sync_cmd_idx);
701247369Ssmh			goto next;
702247369Ssmh		}
703227068Sambrisko		cmd_mfi = &sc->mfi_commands[cmd_tbolt->sync_cmd_idx];
704227068Sambrisko		scsi_io_req = cmd_tbolt->io_request;
705227068Sambrisko
706227068Sambrisko		status = cmd_mfi->cm_frame->dcmd.header.cmd_status;
707227068Sambrisko		extStatus = cmd_mfi->cm_frame->dcmd.header.scsi_status;
708235014Sambrisko		map_tbolt_cmd_status(cmd_mfi, status, extStatus);
709227068Sambrisko
710247369Ssmh		/* mfi_tbolt_return_cmd is handled by mfi complete / return */
711247369Ssmh		if ((cmd_mfi->cm_flags & MFI_CMD_SCSI) != 0 &&
712242681Sambrisko		    (cmd_mfi->cm_flags & MFI_CMD_POLLED) != 0) {
713242681Sambrisko			/* polled LD/SYSPD IO command */
714242681Sambrisko			/* XXX mark okay for now DJA */
715242681Sambrisko			cmd_mfi->cm_frame->header.cmd_status = MFI_STAT_OK;
716247369Ssmh
717242681Sambrisko		} else {
718242681Sambrisko			/* remove command from busy queue if not polled */
719247369Ssmh			if ((cmd_mfi->cm_flags & MFI_ON_MFIQ_BUSY) != 0)
720247369Ssmh				mfi_remove_busy(cmd_mfi);
721242681Sambrisko
722242681Sambrisko			/* complete the command */
723242681Sambrisko			mfi_complete(sc, cmd_mfi);
724227068Sambrisko		}
725227068Sambrisko
726247369Ssmhnext:
727227068Sambrisko		sc->last_reply_idx++;
728227068Sambrisko		if (sc->last_reply_idx >= sc->mfi_max_fw_cmds) {
729227068Sambrisko			MFI_WRITE4(sc, MFI_RPI, sc->last_reply_idx);
730227068Sambrisko			sc->last_reply_idx = 0;
731227068Sambrisko		}
732247369Ssmh
733247369Ssmh		/* Set it back to all 0xfff */
734227068Sambrisko		((union mfi_mpi2_reply_descriptor*)desc)->words =
735227068Sambrisko			~((uint64_t)0x00);
736227068Sambrisko
737227068Sambrisko		num_completed++;
738227068Sambrisko
739227068Sambrisko		/* Get the next reply descriptor */
740227068Sambrisko		desc = (struct mfi_mpi2_reply_header *)
741227068Sambrisko		    ((uintptr_t)sc->reply_frame_pool_align
742227068Sambrisko		    + sc->last_reply_idx * sc->reply_size);
743227068Sambrisko		reply_desc = desc;
744227068Sambrisko		val.word = ((union mfi_mpi2_reply_descriptor*)desc)->words;
745227068Sambrisko		reply_descript_type = reply_desc->ReplyFlags
746227068Sambrisko		    & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
747233711Sambrisko		if (reply_descript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
748227068Sambrisko			break;
749227068Sambrisko	}
750227068Sambrisko
751227068Sambrisko	if (!num_completed)
752227068Sambrisko		return;
753227068Sambrisko
754227068Sambrisko	/* update replyIndex to FW */
755233711Sambrisko	if (sc->last_reply_idx)
756227068Sambrisko		MFI_WRITE4(sc, MFI_RPI, sc->last_reply_idx);
757227068Sambrisko
758227068Sambrisko	return;
759227068Sambrisko}
760227068Sambrisko
761235016Sambrisko/*
762227068Sambrisko * mfi_get_cmd -	Get a command from the free pool
763227068Sambrisko * @instance:		Adapter soft state
764227068Sambrisko *
765227068Sambrisko * Returns a free command from the pool
766227068Sambrisko */
767227068Sambrisko
768235016Sambriskostruct mfi_cmd_tbolt *
769247369Ssmhmfi_tbolt_get_cmd(struct mfi_softc *sc, struct mfi_command *mfi_cmd)
770227068Sambrisko{
771227068Sambrisko	struct mfi_cmd_tbolt *cmd = NULL;
772227068Sambrisko
773227068Sambrisko	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
774227068Sambrisko
775247369Ssmh	if ((cmd = TAILQ_FIRST(&sc->mfi_cmd_tbolt_tqh)) == NULL)
776247369Ssmh		return (NULL);
777227068Sambrisko	TAILQ_REMOVE(&sc->mfi_cmd_tbolt_tqh, cmd, next);
778227068Sambrisko	memset((uint8_t *)cmd->sg_frame, 0, MEGASAS_MAX_SZ_CHAIN_FRAME);
779227068Sambrisko	memset((uint8_t *)cmd->io_request, 0,
780227068Sambrisko	    MEGASAS_THUNDERBOLT_NEW_MSG_SIZE);
781247369Ssmh
782247369Ssmh	cmd->sync_cmd_idx = mfi_cmd->cm_index;
783247369Ssmh	mfi_cmd->cm_extra_frames = cmd->index; /* Frame count used as SMID */
784247369Ssmh	mfi_cmd->cm_flags |= MFI_CMD_TBOLT;
785247369Ssmh
786227068Sambrisko	return cmd;
787227068Sambrisko}
788227068Sambrisko
789227068Sambriskounion mfi_mpi2_request_descriptor *
790227068Sambriskomfi_tbolt_get_request_descriptor(struct mfi_softc *sc, uint16_t index)
791227068Sambrisko{
792227068Sambrisko	uint8_t *p;
793227068Sambrisko
794227068Sambrisko	if (index >= sc->mfi_max_fw_cmds) {
795227068Sambrisko		device_printf(sc->mfi_dev, "Invalid SMID (0x%x)request "
796227068Sambrisko		    "for descriptor\n", index);
797227068Sambrisko		return NULL;
798227068Sambrisko	}
799227068Sambrisko	p = sc->request_desc_pool + sizeof(union mfi_mpi2_request_descriptor)
800227068Sambrisko	    * index;
801227068Sambrisko	memset(p, 0, sizeof(union mfi_mpi2_request_descriptor));
802227068Sambrisko	return (union mfi_mpi2_request_descriptor *)p;
803227068Sambrisko}
804227068Sambrisko
805227068Sambrisko
806233711Sambrisko/* Used to build IOCTL cmd */
807227068Sambriskouint8_t
808227068Sambriskomfi_build_mpt_pass_thru(struct mfi_softc *sc, struct mfi_command *mfi_cmd)
809227068Sambrisko{
810227068Sambrisko	MPI25_IEEE_SGE_CHAIN64 *mpi25_ieee_chain;
811227068Sambrisko	struct mfi_mpi2_request_raid_scsi_io *io_req;
812227068Sambrisko	struct mfi_cmd_tbolt *cmd;
813227068Sambrisko
814247369Ssmh	cmd = mfi_tbolt_get_cmd(sc, mfi_cmd);
815227068Sambrisko	if (!cmd)
816227068Sambrisko		return EBUSY;
817227068Sambrisko	io_req = cmd->io_request;
818227068Sambrisko	mpi25_ieee_chain = (MPI25_IEEE_SGE_CHAIN64 *)&io_req->SGL.IeeeChain;
819227068Sambrisko
820227068Sambrisko	io_req->Function = MPI2_FUNCTION_PASSTHRU_IO_REQUEST;
821227068Sambrisko	io_req->SGLOffset0 = offsetof(struct mfi_mpi2_request_raid_scsi_io,
822227068Sambrisko	    SGL) / 4;
823227068Sambrisko	io_req->ChainOffset = sc->chain_offset_value_for_mpt_ptmsg;
824227068Sambrisko
825227068Sambrisko	mpi25_ieee_chain->Address = mfi_cmd->cm_frame_busaddr;
826227068Sambrisko
827227068Sambrisko	/*
828227068Sambrisko	  In MFI pass thru, nextChainOffset will always be zero to
829227068Sambrisko	  indicate the end of the chain.
830227068Sambrisko	*/
831227068Sambrisko	mpi25_ieee_chain->Flags= MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT
832227068Sambrisko		| MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR;
833227068Sambrisko
834227068Sambrisko	/* setting the length to the maximum length */
835227068Sambrisko	mpi25_ieee_chain->Length = 1024;
836227068Sambrisko
837227068Sambrisko	return 0;
838227068Sambrisko}
839227068Sambrisko
840227068Sambriskovoid
841227068Sambriskomfi_tbolt_build_ldio(struct mfi_softc *sc, struct mfi_command *mfi_cmd,
842227068Sambrisko    struct mfi_cmd_tbolt *cmd)
843227068Sambrisko{
844227068Sambrisko	uint32_t start_lba_lo = 0, start_lba_hi = 0, device_id;
845227068Sambrisko	struct mfi_mpi2_request_raid_scsi_io	*io_request;
846227068Sambrisko	struct IO_REQUEST_INFO io_info;
847227068Sambrisko
848227068Sambrisko	device_id = mfi_cmd->cm_frame->io.header.target_id;
849227068Sambrisko	io_request = cmd->io_request;
850227068Sambrisko	io_request->RaidContext.TargetID = device_id;
851227068Sambrisko	io_request->RaidContext.Status = 0;
852261535Smarkj	io_request->RaidContext.exStatus = 0;
853261535Smarkj	io_request->RaidContext.regLockFlags = 0;
854227068Sambrisko
855227068Sambrisko	start_lba_lo = mfi_cmd->cm_frame->io.lba_lo;
856227068Sambrisko	start_lba_hi = mfi_cmd->cm_frame->io.lba_hi;
857227068Sambrisko
858227068Sambrisko	memset(&io_info, 0, sizeof(struct IO_REQUEST_INFO));
859227068Sambrisko	io_info.ldStartBlock = ((uint64_t)start_lba_hi << 32) | start_lba_lo;
860227068Sambrisko	io_info.numBlocks = mfi_cmd->cm_frame->io.header.data_len;
861227068Sambrisko	io_info.ldTgtId = device_id;
862227068Sambrisko	if ((mfi_cmd->cm_frame->header.flags & MFI_FRAME_DIR_READ) ==
863227068Sambrisko	    MFI_FRAME_DIR_READ)
864227068Sambrisko		io_info.isRead = 1;
865227068Sambrisko
866242681Sambrisko	io_request->RaidContext.timeoutValue
867242681Sambrisko		= MFI_FUSION_FP_DEFAULT_TIMEOUT;
868242681Sambrisko	io_request->Function = MPI2_FUNCTION_LD_IO_REQUEST;
869242681Sambrisko	io_request->DevHandle = device_id;
870242681Sambrisko	cmd->request_desc->header.RequestFlags
871242681Sambrisko		= (MFI_REQ_DESCRIPT_FLAGS_LD_IO
872242681Sambrisko		   << MFI_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
873233711Sambrisko	if ((io_request->IoFlags == 6) && (io_info.numBlocks == 0))
874227068Sambrisko		io_request->RaidContext.RegLockLength = 0x100;
875227068Sambrisko	io_request->DataLength = mfi_cmd->cm_frame->io.header.data_len
876227068Sambrisko	    * MFI_SECTOR_LEN;
877227068Sambrisko}
878227068Sambrisko
879235016Sambriskoint
880235016Sambriskomfi_tbolt_build_io(struct mfi_softc *sc, struct mfi_command *mfi_cmd,
881235016Sambrisko    struct mfi_cmd_tbolt *cmd)
882227068Sambrisko{
883242681Sambrisko	struct mfi_mpi2_request_raid_scsi_io *io_request;
884227068Sambrisko	uint32_t sge_count;
885242681Sambrisko	uint8_t cdb_len;
886242681Sambrisko	int readop;
887242681Sambrisko	u_int64_t lba;
888227068Sambrisko
889242681Sambrisko	io_request = cmd->io_request;
890242681Sambrisko	if (!(mfi_cmd->cm_frame->header.cmd == MFI_CMD_LD_READ
891242681Sambrisko	      || mfi_cmd->cm_frame->header.cmd == MFI_CMD_LD_WRITE))
892242681Sambrisko		return 1;
893227068Sambrisko
894242681Sambrisko	mfi_tbolt_build_ldio(sc, mfi_cmd, cmd);
895227068Sambrisko
896242681Sambrisko	/* Convert to SCSI command CDB */
897242681Sambrisko	bzero(io_request->CDB.CDB32, sizeof(io_request->CDB.CDB32));
898242681Sambrisko	if (mfi_cmd->cm_frame->header.cmd == MFI_CMD_LD_WRITE)
899242681Sambrisko		readop = 0;
900242681Sambrisko	else
901242681Sambrisko		readop = 1;
902227068Sambrisko
903242681Sambrisko	lba =  mfi_cmd->cm_frame->io.lba_hi;
904242681Sambrisko	lba = (lba << 32) + mfi_cmd->cm_frame->io.lba_lo;
905242681Sambrisko	cdb_len = mfi_build_cdb(readop, 0, lba,
906242681Sambrisko	    mfi_cmd->cm_frame->io.header.data_len, io_request->CDB.CDB32);
907242681Sambrisko
908242681Sambrisko	/* Just the CDB length, rest of the Flags are zero */
909227068Sambrisko	io_request->IoFlags = cdb_len;
910227068Sambrisko
911227068Sambrisko	/*
912227068Sambrisko	 * Construct SGL
913227068Sambrisko	 */
914227068Sambrisko	sge_count = mfi_tbolt_make_sgl(sc, mfi_cmd,
915227068Sambrisko	    (pMpi25IeeeSgeChain64_t) &io_request->SGL, cmd);
916227068Sambrisko	if (sge_count > sc->mfi_max_sge) {
917227068Sambrisko		device_printf(sc->mfi_dev, "Error. sge_count (0x%x) exceeds "
918227068Sambrisko		    "max (0x%x) allowed\n", sge_count, sc->mfi_max_sge);
919227068Sambrisko		return 1;
920227068Sambrisko	}
921227068Sambrisko	io_request->RaidContext.numSGE = sge_count;
922227068Sambrisko	io_request->SGLFlags = MPI2_SGE_FLAGS_64_BIT_ADDRESSING;
923227068Sambrisko
924227068Sambrisko	if (mfi_cmd->cm_frame->header.cmd == MFI_CMD_LD_WRITE)
925227068Sambrisko		io_request->Control = MPI2_SCSIIO_CONTROL_WRITE;
926227068Sambrisko	else
927227068Sambrisko		io_request->Control = MPI2_SCSIIO_CONTROL_READ;
928227068Sambrisko
929227068Sambrisko	io_request->SGLOffset0 = offsetof(
930227068Sambrisko	    struct mfi_mpi2_request_raid_scsi_io, SGL)/4;
931227068Sambrisko
932227068Sambrisko	io_request->SenseBufferLowAddress = mfi_cmd->cm_sense_busaddr;
933227068Sambrisko	io_request->SenseBufferLength = MFI_SENSE_LEN;
934242681Sambrisko	io_request->RaidContext.Status = MFI_STAT_INVALID_STATUS;
935242681Sambrisko	io_request->RaidContext.exStatus = MFI_STAT_INVALID_STATUS;
936242681Sambrisko
937227068Sambrisko	return 0;
938227068Sambrisko}
939227068Sambrisko
940227068Sambrisko
941227068Sambriskostatic int
942227068Sambriskomfi_tbolt_make_sgl(struct mfi_softc *sc, struct mfi_command *mfi_cmd,
943233711Sambrisko		   pMpi25IeeeSgeChain64_t sgl_ptr, struct mfi_cmd_tbolt *cmd)
944227068Sambrisko{
945233711Sambrisko	uint8_t i, sg_processed, sg_to_process;
946227068Sambrisko	uint8_t sge_count, sge_idx;
947227068Sambrisko	union mfi_sgl *os_sgl;
948261535Smarkj	pMpi25IeeeSgeChain64_t sgl_end;
949227068Sambrisko
950227068Sambrisko	/*
951227068Sambrisko	 * Return 0 if there is no data transfer
952227068Sambrisko	 */
953227068Sambrisko	if (!mfi_cmd->cm_sg || !mfi_cmd->cm_len) {
954227068Sambrisko	 	device_printf(sc->mfi_dev, "Buffer empty \n");
955227068Sambrisko		return 0;
956227068Sambrisko	}
957227068Sambrisko	os_sgl = mfi_cmd->cm_sg;
958227068Sambrisko	sge_count = mfi_cmd->cm_frame->header.sg_count;
959227068Sambrisko
960227068Sambrisko	if (sge_count > sc->mfi_max_sge) {
961227068Sambrisko		device_printf(sc->mfi_dev, "sgl ptr %p sg_cnt %d \n",
962233711Sambrisko		    os_sgl, sge_count);
963227068Sambrisko		return sge_count;
964227068Sambrisko	}
965227068Sambrisko
966227068Sambrisko	if (sge_count > sc->max_SGEs_in_main_message)
967227068Sambrisko		/* One element to store the chain info */
968227068Sambrisko		sge_idx = sc->max_SGEs_in_main_message - 1;
969227068Sambrisko	else
970227068Sambrisko		sge_idx = sge_count;
971227068Sambrisko
972261535Smarkj	if (sc->mfi_flags & (MFI_FLAGS_INVADER | MFI_FLAGS_FURY)) {
973261535Smarkj		sgl_end = sgl_ptr + (sc->max_SGEs_in_main_message - 1);
974261535Smarkj		sgl_end->Flags = 0;
975261535Smarkj	}
976261535Smarkj
977227068Sambrisko	for (i = 0; i < sge_idx; i++) {
978227068Sambrisko		/*
979233711Sambrisko		 * For 32bit BSD we are getting 32 bit SGL's from OS
980233711Sambrisko		 * but FW only take 64 bit SGL's so copying from 32 bit
981233711Sambrisko		 * SGL's to 64.
982233711Sambrisko		 */
983227068Sambrisko		if (sc->mfi_flags & MFI_FLAGS_SKINNY) {
984227068Sambrisko			sgl_ptr->Length = os_sgl->sg_skinny[i].len;
985227068Sambrisko			sgl_ptr->Address = os_sgl->sg_skinny[i].addr;
986227068Sambrisko		} else {
987227068Sambrisko			sgl_ptr->Length = os_sgl->sg32[i].len;
988233711Sambrisko			sgl_ptr->Address = os_sgl->sg32[i].addr;
989227068Sambrisko		}
990261535Smarkj		if (i == sge_count - 1 &&
991261535Smarkj		    (sc->mfi_flags & (MFI_FLAGS_INVADER | MFI_FLAGS_FURY)))
992261535Smarkj			sgl_ptr->Flags = MPI25_IEEE_SGE_FLAGS_END_OF_LIST;
993261535Smarkj		else
994261535Smarkj			sgl_ptr->Flags = 0;
995227068Sambrisko		sgl_ptr++;
996227068Sambrisko		cmd->io_request->ChainOffset = 0;
997227068Sambrisko	}
998227068Sambrisko
999227068Sambrisko	sg_processed = i;
1000227068Sambrisko
1001227068Sambrisko	if (sg_processed < sge_count) {
1002227068Sambrisko		pMpi25IeeeSgeChain64_t sg_chain;
1003227068Sambrisko		sg_to_process = sge_count - sg_processed;
1004227068Sambrisko		cmd->io_request->ChainOffset =
1005227068Sambrisko		    sc->chain_offset_value_for_main_message;
1006227068Sambrisko		sg_chain = sgl_ptr;
1007227068Sambrisko		/* Prepare chain element */
1008227068Sambrisko		sg_chain->NextChainOffset = 0;
1009261535Smarkj		if (sc->mfi_flags & (MFI_FLAGS_INVADER | MFI_FLAGS_FURY))
1010261535Smarkj			sg_chain->Flags = MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT;
1011261535Smarkj		else
1012261535Smarkj			sg_chain->Flags = MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT |
1013261535Smarkj			    MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR;
1014227068Sambrisko		sg_chain->Length =  (sizeof(MPI2_SGE_IO_UNION) *
1015227068Sambrisko		    (sge_count - sg_processed));
1016233711Sambrisko		sg_chain->Address = cmd->sg_frame_phys_addr;
1017227068Sambrisko		sgl_ptr = (pMpi25IeeeSgeChain64_t)cmd->sg_frame;
1018227068Sambrisko		for (; i < sge_count; i++) {
1019227068Sambrisko			if (sc->mfi_flags & MFI_FLAGS_SKINNY) {
1020227068Sambrisko				sgl_ptr->Length = os_sgl->sg_skinny[i].len;
1021227068Sambrisko				sgl_ptr->Address = os_sgl->sg_skinny[i].addr;
1022233711Sambrisko			} else {
1023227068Sambrisko				sgl_ptr->Length = os_sgl->sg32[i].len;
1024233711Sambrisko				sgl_ptr->Address = os_sgl->sg32[i].addr;
1025227068Sambrisko			}
1026261535Smarkj			if (i == sge_count - 1 &&
1027261535Smarkj			    (sc->mfi_flags &
1028261535Smarkj			    (MFI_FLAGS_INVADER | MFI_FLAGS_FURY)))
1029261535Smarkj				sgl_ptr->Flags =
1030261535Smarkj				    MPI25_IEEE_SGE_FLAGS_END_OF_LIST;
1031261535Smarkj			else
1032261535Smarkj				sgl_ptr->Flags = 0;
1033227068Sambrisko			sgl_ptr++;
1034227068Sambrisko		}
1035227068Sambrisko	}
1036227068Sambrisko	return sge_count;
1037227068Sambrisko}
1038227068Sambrisko
1039227068Sambriskounion mfi_mpi2_request_descriptor *
1040227068Sambriskomfi_build_and_issue_cmd(struct mfi_softc *sc, struct mfi_command *mfi_cmd)
1041227068Sambrisko{
1042227068Sambrisko	struct mfi_cmd_tbolt *cmd;
1043227068Sambrisko	union mfi_mpi2_request_descriptor *req_desc = NULL;
1044227068Sambrisko	uint16_t index;
1045247369Ssmh	cmd = mfi_tbolt_get_cmd(sc, mfi_cmd);
1046247369Ssmh	if (cmd == NULL)
1047247369Ssmh		return (NULL);
1048227068Sambrisko
1049227068Sambrisko	index = cmd->index;
1050227068Sambrisko	req_desc = mfi_tbolt_get_request_descriptor(sc, index-1);
1051247369Ssmh	if (req_desc == NULL) {
1052247369Ssmh		mfi_tbolt_return_cmd(sc, cmd, mfi_cmd);
1053247369Ssmh		return (NULL);
1054247369Ssmh	}
1055247369Ssmh
1056247369Ssmh	if (mfi_tbolt_build_io(sc, mfi_cmd, cmd) != 0) {
1057247369Ssmh		mfi_tbolt_return_cmd(sc, cmd, mfi_cmd);
1058247369Ssmh		return (NULL);
1059247369Ssmh	}
1060227068Sambrisko	req_desc->header.SMID = index;
1061227068Sambrisko	return req_desc;
1062227068Sambrisko}
1063227068Sambrisko
1064227068Sambriskounion mfi_mpi2_request_descriptor *
1065227068Sambriskomfi_tbolt_build_mpt_cmd(struct mfi_softc *sc, struct mfi_command *cmd)
1066227068Sambrisko{
1067227068Sambrisko	union mfi_mpi2_request_descriptor *req_desc = NULL;
1068227068Sambrisko	uint16_t index;
1069227068Sambrisko	if (mfi_build_mpt_pass_thru(sc, cmd)) {
1070227068Sambrisko		device_printf(sc->mfi_dev, "Couldn't build MFI pass thru "
1071227068Sambrisko		    "cmd\n");
1072227068Sambrisko		return NULL;
1073227068Sambrisko	}
1074227068Sambrisko	/* For fusion the frame_count variable is used for SMID */
1075227068Sambrisko	index = cmd->cm_extra_frames;
1076227068Sambrisko
1077227068Sambrisko	req_desc = mfi_tbolt_get_request_descriptor(sc, index - 1);
1078247369Ssmh	if (req_desc == NULL)
1079227068Sambrisko		return NULL;
1080227068Sambrisko
1081237546Skevlo	bzero(req_desc, sizeof(*req_desc));
1082227068Sambrisko	req_desc->header.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO <<
1083227068Sambrisko	    MFI_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
1084227068Sambrisko	req_desc->header.SMID = index;
1085227068Sambrisko	return req_desc;
1086227068Sambrisko}
1087227068Sambrisko
1088227068Sambriskoint
1089227068Sambriskomfi_tbolt_send_frame(struct mfi_softc *sc, struct mfi_command *cm)
1090227068Sambrisko{
1091227068Sambrisko	struct mfi_frame_header *hdr;
1092227068Sambrisko	uint8_t *cdb;
1093227068Sambrisko	union mfi_mpi2_request_descriptor *req_desc = NULL;
1094247369Ssmh	int tm = mfi_polled_cmd_timeout * 1000;
1095227068Sambrisko
1096227068Sambrisko	hdr = &cm->cm_frame->header;
1097227068Sambrisko	cdb = cm->cm_frame->pass.cdb;
1098233711Sambrisko	if (sc->adpreset)
1099227068Sambrisko		return 1;
1100227068Sambrisko	if ((cm->cm_flags & MFI_CMD_POLLED) == 0) {
1101227068Sambrisko		cm->cm_timestamp = time_uptime;
1102227068Sambrisko		mfi_enqueue_busy(cm);
1103242681Sambrisko	} else {	/* still get interrupts for it */
1104235014Sambrisko		hdr->cmd_status = MFI_STAT_INVALID_STATUS;
1105227068Sambrisko		hdr->flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE;
1106227068Sambrisko	}
1107227068Sambrisko
1108227068Sambrisko	if (hdr->cmd == MFI_CMD_PD_SCSI_IO) {
1109227068Sambrisko		/* check for inquiry commands coming from CLI */
1110360843Sdim		if (cdb[0] != 0x28 && cdb[0] != 0x2A) {
1111227068Sambrisko			if ((req_desc = mfi_tbolt_build_mpt_cmd(sc, cm)) ==
1112227068Sambrisko			    NULL) {
1113227068Sambrisko				device_printf(sc->mfi_dev, "Mapping from MFI "
1114227068Sambrisko				    "to MPT Failed \n");
1115227068Sambrisko				return 1;
1116227068Sambrisko			}
1117227068Sambrisko		}
1118227068Sambrisko		else
1119227068Sambrisko			device_printf(sc->mfi_dev, "DJA NA XXX SYSPDIO\n");
1120242681Sambrisko	} else if (hdr->cmd == MFI_CMD_LD_SCSI_IO ||
1121227068Sambrisko	    hdr->cmd == MFI_CMD_LD_READ || hdr->cmd == MFI_CMD_LD_WRITE) {
1122242681Sambrisko		cm->cm_flags |= MFI_CMD_SCSI;
1123227068Sambrisko		if ((req_desc = mfi_build_and_issue_cmd(sc, cm)) == NULL) {
1124227068Sambrisko			device_printf(sc->mfi_dev, "LDIO Failed \n");
1125227068Sambrisko			return 1;
1126227068Sambrisko		}
1127242681Sambrisko	} else if ((req_desc = mfi_tbolt_build_mpt_cmd(sc, cm)) == NULL) {
1128247369Ssmh		device_printf(sc->mfi_dev, "Mapping from MFI to MPT Failed\n");
1129247369Ssmh		return (1);
1130242681Sambrisko	}
1131242681Sambrisko
1132242681Sambrisko	if (cm->cm_flags & MFI_CMD_SCSI) {
1133242681Sambrisko		/*
1134242681Sambrisko		 * LD IO needs to be posted since it doesn't get
1135242681Sambrisko		 * acknowledged via a status update so have the
1136242681Sambrisko		 * controller reply via mfi_tbolt_complete_cmd.
1137242681Sambrisko		 */
1138242681Sambrisko		hdr->flags &= ~MFI_FRAME_DONT_POST_IN_REPLY_QUEUE;
1139242681Sambrisko	}
1140242681Sambrisko
1141227068Sambrisko	MFI_WRITE4(sc, MFI_ILQP, (req_desc->words & 0xFFFFFFFF));
1142227068Sambrisko	MFI_WRITE4(sc, MFI_IHQP, (req_desc->words >>0x20));
1143227068Sambrisko
1144227068Sambrisko	if ((cm->cm_flags & MFI_CMD_POLLED) == 0)
1145227068Sambrisko		return 0;
1146227068Sambrisko
1147247369Ssmh	/*
1148247369Ssmh	 * This is a polled command, so busy-wait for it to complete.
1149247369Ssmh	 *
1150247369Ssmh	 * The value of hdr->cmd_status is updated directly by the hardware
1151298955Spfg	 * so there is no guarantee that mfi_tbolt_complete_cmd is called
1152247369Ssmh	 * prior to this value changing.
1153247369Ssmh	 */
1154235014Sambrisko	while (hdr->cmd_status == MFI_STAT_INVALID_STATUS) {
1155227068Sambrisko		DELAY(1000);
1156227068Sambrisko		tm -= 1;
1157227068Sambrisko		if (tm <= 0)
1158242681Sambrisko			break;
1159242681Sambrisko		if (cm->cm_flags & MFI_CMD_SCSI) {
1160247369Ssmh			/*
1161247369Ssmh			 * Force check reply queue.
1162247369Ssmh			 * This ensures that dump works correctly
1163247369Ssmh			 */
1164242681Sambrisko			mfi_tbolt_complete_cmd(sc);
1165242681Sambrisko		}
1166227068Sambrisko	}
1167235016Sambrisko
1168247369Ssmh	/* ensure the command cleanup has been processed before returning */
1169247369Ssmh	mfi_tbolt_complete_cmd(sc);
1170247369Ssmh
1171235014Sambrisko	if (hdr->cmd_status == MFI_STAT_INVALID_STATUS) {
1172227068Sambrisko		device_printf(sc->mfi_dev, "Frame %p timed out "
1173235014Sambrisko		    "command 0x%X\n", hdr, cm->cm_frame->dcmd.opcode);
1174227068Sambrisko		return (ETIMEDOUT);
1175227068Sambrisko	}
1176227068Sambrisko	return 0;
1177227068Sambrisko}
1178227068Sambrisko
1179235016Sambriskostatic void
1180247369Ssmhmfi_issue_pending_cmds_again(struct mfi_softc *sc)
1181227068Sambrisko{
1182233711Sambrisko	struct mfi_command *cm, *tmp;
1183247369Ssmh	struct mfi_cmd_tbolt *cmd;
1184227068Sambrisko
1185227068Sambrisko	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
1186233711Sambrisko	TAILQ_FOREACH_REVERSE_SAFE(cm, &sc->mfi_busy, BUSYQ, cm_link, tmp) {
1187227068Sambrisko
1188227068Sambrisko		cm->retry_for_fw_reset++;
1189227068Sambrisko
1190227068Sambrisko		/*
1191227068Sambrisko		 * If a command has continuously been tried multiple times
1192227068Sambrisko		 * and causing a FW reset condition, no further recoveries
1193227068Sambrisko		 * should be performed on the controller
1194227068Sambrisko		 */
1195227068Sambrisko		if (cm->retry_for_fw_reset == 3) {
1196247369Ssmh			device_printf(sc->mfi_dev, "megaraid_sas: command %p "
1197247369Ssmh			    "index=%d was tried multiple times during adapter "
1198247369Ssmh			    "reset - Shutting down the HBA\n", cm, cm->cm_index);
1199227068Sambrisko			mfi_kill_hba(sc);
1200227068Sambrisko			sc->hw_crit_error = 1;
1201227068Sambrisko			return;
1202227068Sambrisko		}
1203227068Sambrisko
1204247369Ssmh		mfi_remove_busy(cm);
1205247369Ssmh		if ((cm->cm_flags & MFI_CMD_TBOLT) != 0) {
1206247369Ssmh			if (cm->cm_extra_frames != 0 && cm->cm_extra_frames <=
1207247369Ssmh			    sc->mfi_max_fw_cmds) {
1208247369Ssmh				cmd = sc->mfi_cmd_pool_tbolt[cm->cm_extra_frames - 1];
1209247369Ssmh				mfi_tbolt_return_cmd(sc, cmd, cm);
1210247369Ssmh			} else {
1211247369Ssmh				device_printf(sc->mfi_dev,
1212247369Ssmh				    "Invalid extra_frames: %d detected\n",
1213247369Ssmh				    cm->cm_extra_frames);
1214227068Sambrisko			}
1215227068Sambrisko		}
1216247369Ssmh
1217247369Ssmh		if (cm->cm_frame->dcmd.opcode != MFI_DCMD_CTRL_EVENT_WAIT) {
1218247369Ssmh			device_printf(sc->mfi_dev,
1219247369Ssmh			    "APJ ****requeue command %p index=%d\n",
1220247369Ssmh			    cm, cm->cm_index);
1221247369Ssmh			mfi_requeue_ready(cm);
1222247369Ssmh		} else
1223247369Ssmh			mfi_release_command(cm);
1224227068Sambrisko	}
1225227068Sambrisko	mfi_startio(sc);
1226227068Sambrisko}
1227227068Sambrisko
1228235016Sambriskostatic void
1229247369Ssmhmfi_kill_hba(struct mfi_softc *sc)
1230227068Sambrisko{
1231227068Sambrisko	if (sc->mfi_flags & MFI_FLAGS_TBOLT)
1232247369Ssmh		MFI_WRITE4(sc, 0x00, MFI_STOP_ADP);
1233227068Sambrisko	else
1234247369Ssmh		MFI_WRITE4(sc, MFI_IDB, MFI_STOP_ADP);
1235227068Sambrisko}
1236227068Sambrisko
1237235016Sambriskostatic void
1238235016Sambriskomfi_process_fw_state_chg_isr(void *arg)
1239227068Sambrisko{
1240227068Sambrisko	struct mfi_softc *sc= (struct mfi_softc *)arg;
1241227068Sambrisko	int error, status;
1242227068Sambrisko
1243227068Sambrisko	if (sc->adpreset == 1) {
1244233711Sambrisko		device_printf(sc->mfi_dev, "First stage of FW reset "
1245227068Sambrisko		     "initiated...\n");
1246227068Sambrisko
1247227068Sambrisko		sc->mfi_adp_reset(sc);
1248227068Sambrisko		sc->mfi_enable_intr(sc);
1249227068Sambrisko
1250233711Sambrisko		device_printf(sc->mfi_dev, "First stage of reset complete, "
1251227068Sambrisko		    "second stage initiated...\n");
1252227068Sambrisko
1253227068Sambrisko		sc->adpreset = 2;
1254227068Sambrisko
1255227068Sambrisko		/* waiting for about 20 second before start the second init */
1256233711Sambrisko		for (int wait = 0; wait < 20000; wait++)
1257227068Sambrisko			DELAY(1000);
1258233711Sambrisko		device_printf(sc->mfi_dev, "Second stage of FW reset "
1259227068Sambrisko		     "initiated...\n");
1260233711Sambrisko		while ((status = MFI_READ4(sc, MFI_RSR)) & 0x04);
1261227068Sambrisko
1262227068Sambrisko		sc->mfi_disable_intr(sc);
1263227068Sambrisko
1264227068Sambrisko		/* We expect the FW state to be READY */
1265227068Sambrisko		if (mfi_transition_firmware(sc)) {
1266233711Sambrisko			device_printf(sc->mfi_dev, "controller is not in "
1267233711Sambrisko			    "ready state\n");
1268227068Sambrisko			mfi_kill_hba(sc);
1269247369Ssmh			sc->hw_crit_error = 1;
1270247369Ssmh			return;
1271227068Sambrisko		}
1272247369Ssmh		if ((error = mfi_tbolt_init_MFI_queue(sc)) != 0) {
1273247369Ssmh			device_printf(sc->mfi_dev, "Failed to initialise MFI "
1274247369Ssmh			    "queue\n");
1275247369Ssmh			mfi_kill_hba(sc);
1276247369Ssmh			sc->hw_crit_error = 1;
1277247369Ssmh			return;
1278247369Ssmh		}
1279227068Sambrisko
1280247369Ssmh		/* Init last reply index and max */
1281247369Ssmh		MFI_WRITE4(sc, MFI_RFPI, sc->mfi_max_fw_cmds - 1);
1282247369Ssmh		MFI_WRITE4(sc, MFI_RPI, sc->last_reply_idx);
1283227068Sambrisko
1284227068Sambrisko		sc->mfi_enable_intr(sc);
1285227068Sambrisko		sc->adpreset = 0;
1286247369Ssmh		if (sc->mfi_aen_cm != NULL) {
1287247369Ssmh			free(sc->mfi_aen_cm->cm_data, M_MFIBUF);
1288247369Ssmh			mfi_remove_busy(sc->mfi_aen_cm);
1289227068Sambrisko			mfi_release_command(sc->mfi_aen_cm);
1290227068Sambrisko			sc->mfi_aen_cm = NULL;
1291227068Sambrisko		}
1292247369Ssmh
1293247369Ssmh		if (sc->mfi_map_sync_cm != NULL) {
1294247369Ssmh			mfi_remove_busy(sc->mfi_map_sync_cm);
1295235014Sambrisko			mfi_release_command(sc->mfi_map_sync_cm);
1296235014Sambrisko			sc->mfi_map_sync_cm = NULL;
1297227068Sambrisko		}
1298227068Sambrisko		mfi_issue_pending_cmds_again(sc);
1299227068Sambrisko
1300227068Sambrisko		/*
1301227068Sambrisko		 * Issue pending command can result in adapter being marked
1302227068Sambrisko		 * dead because of too many re-tries. Check for that
1303227068Sambrisko		 * condition before clearing the reset condition on the FW
1304227068Sambrisko		 */
1305227068Sambrisko		if (!sc->hw_crit_error) {
1306227068Sambrisko			/*
1307247369Ssmh			 * Initiate AEN (Asynchronous Event Notification) &
1308247369Ssmh			 * Sync Map
1309227068Sambrisko			 */
1310227068Sambrisko			mfi_aen_setup(sc, sc->last_seq_num);
1311247369Ssmh			mfi_tbolt_sync_map_info(sc);
1312247369Ssmh
1313227068Sambrisko			sc->issuepend_done = 1;
1314233711Sambrisko			device_printf(sc->mfi_dev, "second stage of reset "
1315227068Sambrisko			    "complete, FW is ready now.\n");
1316227068Sambrisko		} else {
1317233711Sambrisko			device_printf(sc->mfi_dev, "second stage of reset "
1318227068Sambrisko			     "never completed, hba was marked offline.\n");
1319227068Sambrisko		}
1320227068Sambrisko	} else {
1321227068Sambrisko		device_printf(sc->mfi_dev, "mfi_process_fw_state_chg_isr "
1322227068Sambrisko		    "called with unhandled value:%d\n", sc->adpreset);
1323227068Sambrisko	}
1324227068Sambrisko}
1325235014Sambrisko
1326235014Sambrisko/*
1327235014Sambrisko * The ThunderBolt HW has an option for the driver to directly
1328235016Sambrisko * access the underlying disks and operate on the RAID.  To
1329235014Sambrisko * do this there needs to be a capability to keep the RAID controller
1330235014Sambrisko * and driver in sync.  The FreeBSD driver does not take advantage
1331235014Sambrisko * of this feature since it adds a lot of complexity and slows down
1332235014Sambrisko * performance.  Performance is gained by using the controller's
1333235014Sambrisko * cache etc.
1334235014Sambrisko *
1335235014Sambrisko * Even though this driver doesn't access the disks directly, an
1336235014Sambrisko * AEN like command is used to inform the RAID firmware to "sync"
1337235014Sambrisko * with all LD's via the MFI_DCMD_LD_MAP_GET_INFO command.  This
1338235014Sambrisko * command in write mode will return when the RAID firmware has
1339235014Sambrisko * detected a change to the RAID state.  Examples of this type
1340235014Sambrisko * of change are removing a disk.  Once the command returns then
1341235014Sambrisko * the driver needs to acknowledge this and "sync" all LD's again.
1342235014Sambrisko * This repeats until we shutdown.  Then we need to cancel this
1343235014Sambrisko * pending command.
1344235014Sambrisko *
1345235014Sambrisko * If this is not done right the RAID firmware will not remove a
1346235014Sambrisko * pulled drive and the RAID won't go degraded etc.  Effectively,
1347235014Sambrisko * stopping any RAID mangement to functions.
1348235014Sambrisko *
1349235014Sambrisko * Doing another LD sync, requires the use of an event since the
1350235014Sambrisko * driver needs to do a mfi_wait_command and can't do that in an
1351235014Sambrisko * interrupt thread.
1352235014Sambrisko *
1353235014Sambrisko * The driver could get the RAID state via the MFI_DCMD_LD_MAP_GET_INFO
1354298955Spfg * That requires a bunch of structure and it is simpler to just do
1355235014Sambrisko * the MFI_DCMD_LD_GET_LIST versus walking the RAID map.
1356235014Sambrisko */
1357235014Sambrisko
1358235014Sambriskovoid
1359235014Sambriskomfi_tbolt_sync_map_info(struct mfi_softc *sc)
1360235014Sambrisko{
1361235014Sambrisko	int error = 0, i;
1362247369Ssmh	struct mfi_command *cmd = NULL;
1363247369Ssmh	struct mfi_dcmd_frame *dcmd = NULL;
1364235014Sambrisko	uint32_t context = 0;
1365247369Ssmh	union mfi_ld_ref *ld_sync = NULL;
1366235014Sambrisko	size_t ld_size;
1367235014Sambrisko	struct mfi_frame_header *hdr;
1368235014Sambrisko	struct mfi_command *cm = NULL;
1369235014Sambrisko	struct mfi_ld_list *list = NULL;
1370235014Sambrisko
1371247369Ssmh	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
1372247369Ssmh
1373235014Sambrisko	if (sc->mfi_map_sync_cm != NULL || sc->cm_map_abort)
1374235014Sambrisko		return;
1375235014Sambrisko
1376235014Sambrisko	error = mfi_dcmd_command(sc, &cm, MFI_DCMD_LD_GET_LIST,
1377235014Sambrisko	    (void **)&list, sizeof(*list));
1378235014Sambrisko	if (error)
1379235014Sambrisko		goto out;
1380235014Sambrisko
1381235014Sambrisko	cm->cm_flags = MFI_CMD_POLLED | MFI_CMD_DATAIN;
1382247369Ssmh
1383235014Sambrisko	if (mfi_wait_command(sc, cm) != 0) {
1384235014Sambrisko		device_printf(sc->mfi_dev, "Failed to get device listing\n");
1385235014Sambrisko		goto out;
1386235014Sambrisko	}
1387235014Sambrisko
1388235014Sambrisko	hdr = &cm->cm_frame->header;
1389235014Sambrisko	if (hdr->cmd_status != MFI_STAT_OK) {
1390235014Sambrisko		device_printf(sc->mfi_dev, "MFI_DCMD_LD_GET_LIST failed %x\n",
1391235014Sambrisko			      hdr->cmd_status);
1392235014Sambrisko		goto out;
1393235014Sambrisko	}
1394235014Sambrisko
1395235014Sambrisko	ld_size = sizeof(*ld_sync) * list->ld_count;
1396235014Sambrisko	ld_sync = (union mfi_ld_ref *) malloc(ld_size, M_MFIBUF,
1397247369Ssmh	     M_NOWAIT | M_ZERO);
1398235040Sambrisko	if (ld_sync == NULL) {
1399235040Sambrisko		device_printf(sc->mfi_dev, "Failed to allocate sync\n");
1400235040Sambrisko		goto out;
1401235040Sambrisko	}
1402247369Ssmh	for (i = 0; i < list->ld_count; i++)
1403235014Sambrisko		ld_sync[i].ref = list->ld_list[i].ld.ref;
1404235014Sambrisko
1405235040Sambrisko	if ((cmd = mfi_dequeue_free(sc)) == NULL) {
1406235040Sambrisko		device_printf(sc->mfi_dev, "Failed to get command\n");
1407235040Sambrisko		free(ld_sync, M_MFIBUF);
1408235040Sambrisko		goto out;
1409235040Sambrisko	}
1410242681Sambrisko
1411235014Sambrisko	context = cmd->cm_frame->header.context;
1412235014Sambrisko	bzero(cmd->cm_frame, sizeof(union mfi_frame));
1413235014Sambrisko	cmd->cm_frame->header.context = context;
1414235014Sambrisko
1415235014Sambrisko	dcmd = &cmd->cm_frame->dcmd;
1416235014Sambrisko	bzero(dcmd->mbox, MFI_MBOX_SIZE);
1417235014Sambrisko	dcmd->header.cmd = MFI_CMD_DCMD;
1418235014Sambrisko	dcmd->header.flags = MFI_FRAME_DIR_WRITE;
1419235014Sambrisko	dcmd->header.timeout = 0;
1420235014Sambrisko	dcmd->header.data_len = ld_size;
1421235014Sambrisko	dcmd->header.scsi_status = 0;
1422235014Sambrisko	dcmd->opcode = MFI_DCMD_LD_MAP_GET_INFO;
1423235014Sambrisko	cmd->cm_sg = &dcmd->sgl;
1424235014Sambrisko	cmd->cm_total_frame_size = MFI_DCMD_FRAME_SIZE;
1425235014Sambrisko	cmd->cm_data = ld_sync;
1426235014Sambrisko	cmd->cm_private = ld_sync;
1427235014Sambrisko
1428235014Sambrisko	cmd->cm_len = ld_size;
1429235014Sambrisko	cmd->cm_complete = mfi_sync_map_complete;
1430235014Sambrisko	sc->mfi_map_sync_cm = cmd;
1431235014Sambrisko
1432235014Sambrisko	cmd->cm_flags = MFI_CMD_DATAOUT;
1433235014Sambrisko	cmd->cm_frame->dcmd.mbox[0] = list->ld_count;
1434235014Sambrisko	cmd->cm_frame->dcmd.mbox[1] = MFI_DCMD_MBOX_PEND_FLAG;
1435235014Sambrisko
1436235014Sambrisko	if ((error = mfi_mapcmd(sc, cmd)) != 0) {
1437235014Sambrisko		device_printf(sc->mfi_dev, "failed to send map sync\n");
1438235040Sambrisko		free(ld_sync, M_MFIBUF);
1439235040Sambrisko		sc->mfi_map_sync_cm = NULL;
1440247369Ssmh		mfi_release_command(cmd);
1441235040Sambrisko		goto out;
1442235014Sambrisko	}
1443235014Sambrisko
1444235014Sambriskoout:
1445235014Sambrisko	if (list)
1446235014Sambrisko		free(list, M_MFIBUF);
1447235014Sambrisko	if (cm)
1448235014Sambrisko		mfi_release_command(cm);
1449235014Sambrisko}
1450235014Sambrisko
1451235014Sambriskostatic void
1452235014Sambriskomfi_sync_map_complete(struct mfi_command *cm)
1453235014Sambrisko{
1454235014Sambrisko	struct mfi_frame_header *hdr;
1455235014Sambrisko	struct mfi_softc *sc;
1456235014Sambrisko	int aborted = 0;
1457235014Sambrisko
1458235014Sambrisko	sc = cm->cm_sc;
1459235014Sambrisko	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
1460235014Sambrisko
1461235014Sambrisko	hdr = &cm->cm_frame->header;
1462235014Sambrisko
1463235014Sambrisko	if (sc->mfi_map_sync_cm == NULL)
1464235014Sambrisko		return;
1465235014Sambrisko
1466235014Sambrisko	if (sc->cm_map_abort ||
1467235014Sambrisko	    hdr->cmd_status == MFI_STAT_INVALID_STATUS) {
1468235014Sambrisko		sc->cm_map_abort = 0;
1469235014Sambrisko		aborted = 1;
1470235014Sambrisko	}
1471235014Sambrisko
1472235014Sambrisko	free(cm->cm_data, M_MFIBUF);
1473247369Ssmh	wakeup(&sc->mfi_map_sync_cm);
1474235014Sambrisko	sc->mfi_map_sync_cm = NULL;
1475235014Sambrisko	mfi_release_command(cm);
1476235014Sambrisko
1477235014Sambrisko	/* set it up again so the driver can catch more events */
1478247369Ssmh	if (!aborted)
1479235014Sambrisko		mfi_queue_map_sync(sc);
1480235014Sambrisko}
1481235014Sambrisko
1482235014Sambriskostatic void
1483235014Sambriskomfi_queue_map_sync(struct mfi_softc *sc)
1484235014Sambrisko{
1485235014Sambrisko	mtx_assert(&sc->mfi_io_lock, MA_OWNED);
1486235014Sambrisko	taskqueue_enqueue(taskqueue_swi, &sc->mfi_map_sync_task);
1487235014Sambrisko}
1488235014Sambrisko
1489235014Sambriskovoid
1490235014Sambriskomfi_handle_map_sync(void *context, int pending)
1491235014Sambrisko{
1492235014Sambrisko	struct mfi_softc *sc;
1493235014Sambrisko
1494235014Sambrisko	sc = context;
1495247369Ssmh	mtx_lock(&sc->mfi_io_lock);
1496235014Sambrisko	mfi_tbolt_sync_map_info(sc);
1497247369Ssmh	mtx_unlock(&sc->mfi_io_lock);
1498235014Sambrisko}
1499