1265555Sambrisko/*
2282531Skadesai * Copyright (c) 2015, AVAGO Tech. All rights reserved. Author: Marian Choy
3272744Skadesai * Copyright (c) 2014, LSI Corp. All rights reserved. Author: Marian Choy
4282531Skadesai * Support: freebsdraid@avagotech.com
5265555Sambrisko *
6265555Sambrisko * Redistribution and use in source and binary forms, with or without
7272744Skadesai * modification, are permitted provided that the following conditions are
8272744Skadesai * met:
9265555Sambrisko *
10272744Skadesai * 1. Redistributions of source code must retain the above copyright notice,
11272744Skadesai * this list of conditions and the following disclaimer. 2. Redistributions
12272744Skadesai * in binary form must reproduce the above copyright notice, this list of
13272744Skadesai * conditions and the following disclaimer in the documentation and/or other
14272744Skadesai * materials provided with the distribution. 3. Neither the name of the
15272744Skadesai * <ORGANIZATION> nor the names of its contributors may be used to endorse or
16272744Skadesai * promote products derived from this software without specific prior written
17272744Skadesai * permission.
18265555Sambrisko *
19272744Skadesai * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20272744Skadesai * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21272744Skadesai * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22272744Skadesai * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23272744Skadesai * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24272744Skadesai * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25272744Skadesai * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26272744Skadesai * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27272744Skadesai * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28272744Skadesai * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29265555Sambrisko * POSSIBILITY OF SUCH DAMAGE.
30265555Sambrisko *
31272744Skadesai * The views and conclusions contained in the software and documentation are
32272744Skadesai * those of the authors and should not be interpreted as representing
33265555Sambrisko * official policies,either expressed or implied, of the FreeBSD Project.
34265555Sambrisko *
35282531Skadesai * Send feedback to: <megaraidfbsd@avagotech.com> Mail to: AVAGO TECHNOLOGIES, 1621
36272744Skadesai * Barber Lane, Milpitas, CA 95035 ATTN: MegaRaid FreeBSD
37265555Sambrisko *
38265555Sambrisko */
39265555Sambrisko
40265555Sambrisko#include <sys/cdefs.h>
41265555Sambrisko__FBSDID("$FreeBSD$");
42265555Sambrisko
43265555Sambrisko#include <dev/mrsas/mrsas.h>
44265555Sambrisko#include <dev/mrsas/mrsas_ioctl.h>
45265555Sambrisko
46272744Skadesai/*
47272744Skadesai * Function prototypes
48265555Sambrisko */
49272744Skadesaiint	mrsas_alloc_mfi_cmds(struct mrsas_softc *sc);
50272744Skadesaiint	mrsas_passthru(struct mrsas_softc *sc, void *arg, u_long ioctlCmd);
51272744Skadesaivoid	mrsas_free_ioc_cmd(struct mrsas_softc *sc);
52272744Skadesaivoid	mrsas_free_frame(struct mrsas_softc *sc, struct mrsas_mfi_cmd *cmd);
53272744Skadesaivoid   *mrsas_alloc_frame(struct mrsas_softc *sc, struct mrsas_mfi_cmd *cmd);
54265555Sambriskostatic int mrsas_create_frame_pool(struct mrsas_softc *sc);
55272744Skadesaistatic void
56272744Skadesaimrsas_alloc_cb(void *arg, bus_dma_segment_t *segs,
57272744Skadesai    int nsegs, int error);
58265555Sambrisko
59272744Skadesaiextern struct mrsas_mfi_cmd *mrsas_get_mfi_cmd(struct mrsas_softc *sc);
60265555Sambriskoextern void mrsas_release_mfi_cmd(struct mrsas_mfi_cmd *cmd);
61272744Skadesaiextern int
62272744Skadesaimrsas_issue_blocked_cmd(struct mrsas_softc *sc,
63265555Sambrisko    struct mrsas_mfi_cmd *cmd);
64265555Sambrisko
65272744Skadesai/*
66272744Skadesai * mrsas_passthru:	Handle pass-through commands
67272744Skadesai * input:			Adapter instance soft state argument pointer
68265555Sambrisko *
69272744Skadesai * This function is called from mrsas_ioctl() to handle pass-through and ioctl
70272744Skadesai * commands to Firmware.
71265555Sambrisko */
72272744Skadesaiint
73272744Skadesaimrsas_passthru(struct mrsas_softc *sc, void *arg, u_long ioctlCmd)
74265555Sambrisko{
75272744Skadesai	struct mrsas_iocpacket *user_ioc = (struct mrsas_iocpacket *)arg;
76272744Skadesai
77272737Skadesai#ifdef COMPAT_FREEBSD32
78272744Skadesai	struct mrsas_iocpacket32 *user_ioc32 = (struct mrsas_iocpacket32 *)arg;
79272744Skadesai
80272737Skadesai#endif
81272744Skadesai	union mrsas_frame *in_cmd = (union mrsas_frame *)&(user_ioc->frame.raw);
82272744Skadesai	struct mrsas_mfi_cmd *cmd = NULL;
83272744Skadesai	bus_dma_tag_t ioctl_data_tag[MAX_IOCTL_SGE];
84272744Skadesai	bus_dmamap_t ioctl_data_dmamap[MAX_IOCTL_SGE];
85272744Skadesai	void *ioctl_data_mem[MAX_IOCTL_SGE];
86272744Skadesai	bus_addr_t ioctl_data_phys_addr[MAX_IOCTL_SGE];
87272744Skadesai	bus_dma_tag_t ioctl_sense_tag = 0;
88272744Skadesai	bus_dmamap_t ioctl_sense_dmamap = 0;
89297862Spfg	void *ioctl_sense_mem = NULL;
90272744Skadesai	bus_addr_t ioctl_sense_phys_addr = 0;
91272744Skadesai	int i, ioctl_data_size = 0, ioctl_sense_size, ret = 0;
92272744Skadesai	struct mrsas_sge32 *kern_sge32;
93272744Skadesai	unsigned long *sense_ptr;
94272744Skadesai	uint8_t *iov_base_ptrin = NULL;
95272744Skadesai	size_t iov_len = 0;
96265555Sambrisko
97272744Skadesai	/*
98272744Skadesai	 * Check for NOP from MegaCli... MegaCli can issue a DCMD of 0.  In
99272744Skadesai	 * this case do nothing and return 0 to it as status.
100272744Skadesai	 */
101272744Skadesai	if (in_cmd->dcmd.opcode == 0) {
102272744Skadesai		device_printf(sc->mrsas_dev, "In %s() Got a NOP\n", __func__);
103272744Skadesai		user_ioc->frame.hdr.cmd_status = MFI_STAT_OK;
104272744Skadesai		return (0);
105272744Skadesai	}
106272744Skadesai	/* Validate SGL length */
107272744Skadesai	if (user_ioc->sge_count > MAX_IOCTL_SGE) {
108272744Skadesai		device_printf(sc->mrsas_dev, "In %s() SGL is too long (%d > 8).\n",
109272744Skadesai		    __func__, user_ioc->sge_count);
110272744Skadesai		return (ENOENT);
111272744Skadesai	}
112272744Skadesai	/* Get a command */
113272744Skadesai	cmd = mrsas_get_mfi_cmd(sc);
114272744Skadesai	if (!cmd) {
115272744Skadesai		device_printf(sc->mrsas_dev, "Failed to get a free cmd for IOCTL\n");
116272744Skadesai		return (ENOMEM);
117272744Skadesai	}
118272744Skadesai	/*
119272744Skadesai	 * User's IOCTL packet has 2 frames (maximum). Copy those two frames
120272744Skadesai	 * into our cmd's frames. cmd->frame's context will get overwritten
121272744Skadesai	 * when we copy from user's frames. So set that value alone
122272744Skadesai	 * separately
123272744Skadesai	 */
124272744Skadesai	memcpy(cmd->frame, user_ioc->frame.raw, 2 * MEGAMFI_FRAME_SIZE);
125272744Skadesai	cmd->frame->hdr.context = cmd->index;
126272744Skadesai	cmd->frame->hdr.pad_0 = 0;
127272744Skadesai	cmd->frame->hdr.flags &= ~(MFI_FRAME_IEEE | MFI_FRAME_SGL64 |
128272744Skadesai	    MFI_FRAME_SENSE64);
129265555Sambrisko
130272744Skadesai	/*
131272744Skadesai	 * The management interface between applications and the fw uses MFI
132272744Skadesai	 * frames. E.g, RAID configuration changes, LD property changes etc
133272744Skadesai	 * are accomplishes through different kinds of MFI frames. The driver
134272744Skadesai	 * needs to care only about substituting user buffers with kernel
135272744Skadesai	 * buffers in SGLs. The location of SGL is embedded in the struct
136272744Skadesai	 * iocpacket itself.
137272744Skadesai	 */
138272744Skadesai	kern_sge32 = (struct mrsas_sge32 *)
139272744Skadesai	    ((unsigned long)cmd->frame + user_ioc->sgl_off);
140265555Sambrisko
141299671Skadesai	memset(ioctl_data_tag, 0, (sizeof(bus_dma_tag_t) * MAX_IOCTL_SGE));
142299671Skadesai	memset(ioctl_data_dmamap, 0, (sizeof(bus_dmamap_t) * MAX_IOCTL_SGE));
143299671Skadesai	memset(ioctl_data_mem, 0, (sizeof(void *) * MAX_IOCTL_SGE));
144299671Skadesai	memset(ioctl_data_phys_addr, 0, (sizeof(bus_addr_t) * MAX_IOCTL_SGE));
145299671Skadesai
146272744Skadesai	/*
147272744Skadesai	 * For each user buffer, create a mirror buffer and copy in
148272744Skadesai	 */
149272744Skadesai	for (i = 0; i < user_ioc->sge_count; i++) {
150272744Skadesai		if (ioctlCmd == MRSAS_IOC_FIRMWARE_PASS_THROUGH64) {
151272744Skadesai			if (!user_ioc->sgl[i].iov_len)
152272744Skadesai				continue;
153272744Skadesai			ioctl_data_size = user_ioc->sgl[i].iov_len;
154272737Skadesai#ifdef COMPAT_FREEBSD32
155272744Skadesai		} else {
156272744Skadesai			if (!user_ioc32->sgl[i].iov_len)
157272744Skadesai				continue;
158272744Skadesai			ioctl_data_size = user_ioc32->sgl[i].iov_len;
159272737Skadesai#endif
160272744Skadesai		}
161272744Skadesai		if (bus_dma_tag_create(sc->mrsas_parent_tag,
162272744Skadesai		    1, 0,
163272744Skadesai		    BUS_SPACE_MAXADDR_32BIT,
164272744Skadesai		    BUS_SPACE_MAXADDR,
165272744Skadesai		    NULL, NULL,
166272744Skadesai		    ioctl_data_size,
167272744Skadesai		    1,
168272744Skadesai		    ioctl_data_size,
169272744Skadesai		    BUS_DMA_ALLOCNOW,
170272744Skadesai		    NULL, NULL,
171272744Skadesai		    &ioctl_data_tag[i])) {
172272744Skadesai			device_printf(sc->mrsas_dev, "Cannot allocate ioctl data tag\n");
173272741Skadesai			ret = ENOMEM;
174272741Skadesai			goto out;
175272744Skadesai		}
176272744Skadesai		if (bus_dmamem_alloc(ioctl_data_tag[i], (void **)&ioctl_data_mem[i],
177272744Skadesai		    (BUS_DMA_NOWAIT | BUS_DMA_ZERO), &ioctl_data_dmamap[i])) {
178272744Skadesai			device_printf(sc->mrsas_dev, "Cannot allocate ioctl data mem\n");
179272741Skadesai			ret = ENOMEM;
180272741Skadesai			goto out;
181272744Skadesai		}
182272744Skadesai		if (bus_dmamap_load(ioctl_data_tag[i], ioctl_data_dmamap[i],
183272744Skadesai		    ioctl_data_mem[i], ioctl_data_size, mrsas_alloc_cb,
184272744Skadesai		    &ioctl_data_phys_addr[i], BUS_DMA_NOWAIT)) {
185272744Skadesai			device_printf(sc->mrsas_dev, "Cannot load ioctl data mem\n");
186272744Skadesai			ret = ENOMEM;
187272744Skadesai			goto out;
188272744Skadesai		}
189272744Skadesai		/* Save the physical address and length */
190272744Skadesai		kern_sge32[i].phys_addr = (u_int32_t)ioctl_data_phys_addr[i];
191265555Sambrisko
192272744Skadesai		if (ioctlCmd == MRSAS_IOC_FIRMWARE_PASS_THROUGH64) {
193272744Skadesai			kern_sge32[i].length = user_ioc->sgl[i].iov_len;
194265555Sambrisko
195272744Skadesai			iov_base_ptrin = user_ioc->sgl[i].iov_base;
196272744Skadesai			iov_len = user_ioc->sgl[i].iov_len;
197272737Skadesai#ifdef COMPAT_FREEBSD32
198272744Skadesai		} else {
199272744Skadesai			kern_sge32[i].length = user_ioc32->sgl[i].iov_len;
200272737Skadesai
201272744Skadesai			iov_base_ptrin = PTRIN(user_ioc32->sgl[i].iov_base);
202272744Skadesai			iov_len = user_ioc32->sgl[i].iov_len;
203272737Skadesai#endif
204272744Skadesai		}
205272744Skadesai
206272744Skadesai		/* Copy in data from user space */
207272744Skadesai		ret = copyin(iov_base_ptrin, ioctl_data_mem[i], iov_len);
208272744Skadesai		if (ret) {
209272744Skadesai			device_printf(sc->mrsas_dev, "IOCTL copyin failed!\n");
210272744Skadesai			goto out;
211272744Skadesai		}
212272737Skadesai	}
213272737Skadesai
214272744Skadesai	ioctl_sense_size = user_ioc->sense_len;
215265555Sambrisko
216272744Skadesai	if (user_ioc->sense_len) {
217272744Skadesai		if (bus_dma_tag_create(sc->mrsas_parent_tag,
218272744Skadesai		    1, 0,
219272744Skadesai		    BUS_SPACE_MAXADDR_32BIT,
220272744Skadesai		    BUS_SPACE_MAXADDR,
221272744Skadesai		    NULL, NULL,
222272744Skadesai		    ioctl_sense_size,
223272744Skadesai		    1,
224272744Skadesai		    ioctl_sense_size,
225272744Skadesai		    BUS_DMA_ALLOCNOW,
226272744Skadesai		    NULL, NULL,
227272744Skadesai		    &ioctl_sense_tag)) {
228272744Skadesai			device_printf(sc->mrsas_dev, "Cannot allocate ioctl sense tag\n");
229272744Skadesai			ret = ENOMEM;
230272741Skadesai			goto out;
231272744Skadesai		}
232272744Skadesai		if (bus_dmamem_alloc(ioctl_sense_tag, (void **)&ioctl_sense_mem,
233272744Skadesai		    (BUS_DMA_NOWAIT | BUS_DMA_ZERO), &ioctl_sense_dmamap)) {
234272744Skadesai			device_printf(sc->mrsas_dev, "Cannot allocate ioctl sense mem\n");
235272744Skadesai			ret = ENOMEM;
236272741Skadesai			goto out;
237272744Skadesai		}
238272744Skadesai		if (bus_dmamap_load(ioctl_sense_tag, ioctl_sense_dmamap,
239272744Skadesai		    ioctl_sense_mem, ioctl_sense_size, mrsas_alloc_cb,
240272744Skadesai		    &ioctl_sense_phys_addr, BUS_DMA_NOWAIT)) {
241272744Skadesai			device_printf(sc->mrsas_dev, "Cannot load ioctl sense mem\n");
242272744Skadesai			ret = ENOMEM;
243272741Skadesai			goto out;
244272744Skadesai		}
245272744Skadesai		sense_ptr =
246272744Skadesai		    (unsigned long *)((unsigned long)cmd->frame + user_ioc->sense_off);
247282531Skadesai		*sense_ptr = ioctl_sense_phys_addr;
248272744Skadesai	}
249272744Skadesai	/*
250272744Skadesai	 * Set the sync_cmd flag so that the ISR knows not to complete this
251272744Skadesai	 * cmd to the SCSI mid-layer
252272744Skadesai	 */
253272744Skadesai	cmd->sync_cmd = 1;
254299666Skadesai	ret = mrsas_issue_blocked_cmd(sc, cmd);
255299666Skadesai	if (ret == ETIMEDOUT) {
256299666Skadesai		mrsas_dprint(sc, MRSAS_OCR,
257299666Skadesai		    "IOCTL command is timed out, initiating OCR\n");
258299666Skadesai		sc->do_timedout_reset = MFI_DCMD_TIMEOUT_OCR;
259299666Skadesai		ret = EAGAIN;
260299666Skadesai		goto out;
261299666Skadesai	}
262272744Skadesai	cmd->sync_cmd = 0;
263265555Sambrisko
264272744Skadesai	/*
265272744Skadesai	 * copy out the kernel buffers to user buffers
266272744Skadesai	 */
267272744Skadesai	for (i = 0; i < user_ioc->sge_count; i++) {
268272744Skadesai		if (ioctlCmd == MRSAS_IOC_FIRMWARE_PASS_THROUGH64) {
269272744Skadesai			iov_base_ptrin = user_ioc->sgl[i].iov_base;
270272744Skadesai			iov_len = user_ioc->sgl[i].iov_len;
271272737Skadesai#ifdef COMPAT_FREEBSD32
272272744Skadesai		} else {
273272744Skadesai			iov_base_ptrin = PTRIN(user_ioc32->sgl[i].iov_base);
274272744Skadesai			iov_len = user_ioc32->sgl[i].iov_len;
275272737Skadesai#endif
276272744Skadesai		}
277272737Skadesai
278272744Skadesai		ret = copyout(ioctl_data_mem[i], iov_base_ptrin, iov_len);
279272744Skadesai		if (ret) {
280272744Skadesai			device_printf(sc->mrsas_dev, "IOCTL copyout failed!\n");
281272744Skadesai			goto out;
282272744Skadesai		}
283272744Skadesai	}
284265555Sambrisko
285272744Skadesai	/*
286272744Skadesai	 * copy out the sense
287272744Skadesai	 */
288272744Skadesai	if (user_ioc->sense_len) {
289272744Skadesai		/*
290272744Skadesai		 * sense_buff points to the location that has the user sense
291272744Skadesai		 * buffer address
292272744Skadesai		 */
293272744Skadesai		sense_ptr = (unsigned long *)((unsigned long)user_ioc->frame.raw +
294272744Skadesai		    user_ioc->sense_off);
295272744Skadesai		ret = copyout(ioctl_sense_mem, (unsigned long *)*sense_ptr,
296272744Skadesai		    user_ioc->sense_len);
297272744Skadesai		if (ret) {
298272744Skadesai			device_printf(sc->mrsas_dev, "IOCTL sense copyout failed!\n");
299272744Skadesai			goto out;
300272744Skadesai		}
301272744Skadesai	}
302272744Skadesai	/*
303272744Skadesai	 * Return command status to user space
304272744Skadesai	 */
305272744Skadesai	memcpy(&user_ioc->frame.hdr.cmd_status, &cmd->frame->hdr.cmd_status,
306272744Skadesai	    sizeof(u_int8_t));
307265555Sambrisko
308265555Sambriskoout:
309272744Skadesai	/*
310272744Skadesai	 * Release sense buffer
311272744Skadesai	 */
312282531Skadesai	if (user_ioc->sense_len) {
313282531Skadesai		if (ioctl_sense_phys_addr)
314282531Skadesai			bus_dmamap_unload(ioctl_sense_tag, ioctl_sense_dmamap);
315282531Skadesai		if (ioctl_sense_mem != NULL)
316282531Skadesai			bus_dmamem_free(ioctl_sense_tag, ioctl_sense_mem, ioctl_sense_dmamap);
317282531Skadesai		if (ioctl_sense_tag != NULL)
318282531Skadesai			bus_dma_tag_destroy(ioctl_sense_tag);
319282531Skadesai	}
320272744Skadesai	/*
321272744Skadesai	 * Release data buffers
322272744Skadesai	 */
323272744Skadesai	for (i = 0; i < user_ioc->sge_count; i++) {
324272744Skadesai		if (ioctlCmd == MRSAS_IOC_FIRMWARE_PASS_THROUGH64) {
325272744Skadesai			if (!user_ioc->sgl[i].iov_len)
326272744Skadesai				continue;
327272741Skadesai#ifdef COMPAT_FREEBSD32
328272744Skadesai		} else {
329272744Skadesai			if (!user_ioc32->sgl[i].iov_len)
330272744Skadesai				continue;
331272741Skadesai#endif
332272744Skadesai		}
333272744Skadesai		if (ioctl_data_phys_addr[i])
334272744Skadesai			bus_dmamap_unload(ioctl_data_tag[i], ioctl_data_dmamap[i]);
335272744Skadesai		if (ioctl_data_mem[i] != NULL)
336272744Skadesai			bus_dmamem_free(ioctl_data_tag[i], ioctl_data_mem[i],
337272744Skadesai			    ioctl_data_dmamap[i]);
338272744Skadesai		if (ioctl_data_tag[i] != NULL)
339272744Skadesai			bus_dma_tag_destroy(ioctl_data_tag[i]);
340272744Skadesai	}
341272744Skadesai	/* Free command */
342272744Skadesai	mrsas_release_mfi_cmd(cmd);
343265555Sambrisko
344272744Skadesai	return (ret);
345265555Sambrisko}
346265555Sambrisko
347272744Skadesai/*
348272744Skadesai * mrsas_alloc_mfi_cmds:	Allocates the command packets
349272744Skadesai * input:					Adapter instance soft state
350265555Sambrisko *
351265555Sambrisko * Each IOCTL or passthru command that is issued to the FW are wrapped in a
352272744Skadesai * local data structure called mrsas_mfi_cmd.  The frame embedded in this
353272744Skadesai * mrsas_mfi is issued to FW. The array is used only to look up the
354265555Sambrisko * mrsas_mfi_cmd given the context. The free commands are maintained in a
355265555Sambrisko * linked list.
356265555Sambrisko */
357272744Skadesaiint
358272744Skadesaimrsas_alloc_mfi_cmds(struct mrsas_softc *sc)
359265555Sambrisko{
360272744Skadesai	int i, j;
361272744Skadesai	u_int32_t max_cmd;
362272744Skadesai	struct mrsas_mfi_cmd *cmd;
363265555Sambrisko
364272744Skadesai	max_cmd = MRSAS_MAX_MFI_CMDS;
365265555Sambrisko
366272744Skadesai	/*
367272744Skadesai	 * sc->mfi_cmd_list is an array of struct mrsas_mfi_cmd pointers.
368272744Skadesai	 * Allocate the dynamic array first and then allocate individual
369272744Skadesai	 * commands.
370272744Skadesai	 */
371272744Skadesai	sc->mfi_cmd_list = malloc(sizeof(struct mrsas_mfi_cmd *) * max_cmd, M_MRSAS, M_NOWAIT);
372272744Skadesai	if (!sc->mfi_cmd_list) {
373272744Skadesai		device_printf(sc->mrsas_dev, "Cannot alloc memory for mfi_cmd cmd_list.\n");
374272744Skadesai		return (ENOMEM);
375272744Skadesai	}
376272744Skadesai	memset(sc->mfi_cmd_list, 0, sizeof(struct mrsas_mfi_cmd *) * max_cmd);
377272744Skadesai	for (i = 0; i < max_cmd; i++) {
378272744Skadesai		sc->mfi_cmd_list[i] = malloc(sizeof(struct mrsas_mfi_cmd),
379272744Skadesai		    M_MRSAS, M_NOWAIT);
380272744Skadesai		if (!sc->mfi_cmd_list[i]) {
381272744Skadesai			for (j = 0; j < i; j++)
382272744Skadesai				free(sc->mfi_cmd_list[j], M_MRSAS);
383272744Skadesai			free(sc->mfi_cmd_list, M_MRSAS);
384272744Skadesai			sc->mfi_cmd_list = NULL;
385272744Skadesai			return (ENOMEM);
386272744Skadesai		}
387272744Skadesai	}
388265555Sambrisko
389272744Skadesai	for (i = 0; i < max_cmd; i++) {
390272744Skadesai		cmd = sc->mfi_cmd_list[i];
391272744Skadesai		memset(cmd, 0, sizeof(struct mrsas_mfi_cmd));
392272744Skadesai		cmd->index = i;
393272744Skadesai		cmd->ccb_ptr = NULL;
394272744Skadesai		cmd->sc = sc;
395272744Skadesai		TAILQ_INSERT_TAIL(&(sc->mrsas_mfi_cmd_list_head), cmd, next);
396272744Skadesai	}
397265555Sambrisko
398272744Skadesai	/* create a frame pool and assign one frame to each command */
399272744Skadesai	if (mrsas_create_frame_pool(sc)) {
400272744Skadesai		device_printf(sc->mrsas_dev, "Cannot allocate DMA frame pool.\n");
401272744Skadesai		/* Free the frames */
402272744Skadesai		for (i = 0; i < MRSAS_MAX_MFI_CMDS; i++) {
403272744Skadesai			cmd = sc->mfi_cmd_list[i];
404272744Skadesai			mrsas_free_frame(sc, cmd);
405272744Skadesai		}
406272744Skadesai		if (sc->mficmd_frame_tag != NULL)
407272744Skadesai			bus_dma_tag_destroy(sc->mficmd_frame_tag);
408272744Skadesai		return (ENOMEM);
409272744Skadesai	}
410272744Skadesai	return (0);
411265555Sambrisko}
412265555Sambrisko
413272744Skadesai/*
414272744Skadesai * mrsas_create_frame_pool:	Creates DMA pool for cmd frames
415272744Skadesai * input:					Adapter soft state
416265555Sambrisko *
417265555Sambrisko * Each command packet has an embedded DMA memory buffer that is used for
418265555Sambrisko * filling MFI frame and the SG list that immediately follows the frame. This
419265555Sambrisko * function creates those DMA memory buffers for each command packet by using
420272744Skadesai * PCI pool facility. pad_0 is initialized to 0 to prevent corrupting value
421265555Sambrisko * of context and could cause FW crash.
422265555Sambrisko */
423272744Skadesaistatic int
424272744Skadesaimrsas_create_frame_pool(struct mrsas_softc *sc)
425265555Sambrisko{
426272744Skadesai	int i;
427272744Skadesai	struct mrsas_mfi_cmd *cmd;
428265555Sambrisko
429272744Skadesai	if (bus_dma_tag_create(sc->mrsas_parent_tag,
430272744Skadesai	    1, 0,
431272744Skadesai	    BUS_SPACE_MAXADDR_32BIT,
432272744Skadesai	    BUS_SPACE_MAXADDR,
433272744Skadesai	    NULL, NULL,
434272744Skadesai	    MRSAS_MFI_FRAME_SIZE,
435272744Skadesai	    1,
436272744Skadesai	    MRSAS_MFI_FRAME_SIZE,
437272744Skadesai	    BUS_DMA_ALLOCNOW,
438272744Skadesai	    NULL, NULL,
439272744Skadesai	    &sc->mficmd_frame_tag)) {
440272744Skadesai		device_printf(sc->mrsas_dev, "Cannot create MFI frame tag\n");
441272744Skadesai		return (ENOMEM);
442272744Skadesai	}
443272744Skadesai	for (i = 0; i < MRSAS_MAX_MFI_CMDS; i++) {
444272744Skadesai		cmd = sc->mfi_cmd_list[i];
445272744Skadesai		cmd->frame = mrsas_alloc_frame(sc, cmd);
446272744Skadesai		if (cmd->frame == NULL) {
447272744Skadesai			device_printf(sc->mrsas_dev, "Cannot alloc MFI frame memory\n");
448272744Skadesai			return (ENOMEM);
449272744Skadesai		}
450299671Skadesai		/*
451299671Skadesai		 * For MFI controllers.
452299671Skadesai		 * max_num_sge = 60
453299671Skadesai		 * max_sge_sz  = 16 byte (sizeof megasas_sge_skinny)
454299671Skadesai		 * Totl 960 byte (15 MFI frame of 64 byte)
455299671Skadesai		 *
456299671Skadesai		 * Fusion adapter require only 3 extra frame.
457299671Skadesai		 * max_num_sge = 16 (defined as MAX_IOCTL_SGE)
458299671Skadesai		 * max_sge_sz  = 12 byte (sizeof  megasas_sge64)
459299671Skadesai		 * Total 192 byte (3 MFI frame of 64 byte)
460299671Skadesai		 */
461272744Skadesai		memset(cmd->frame, 0, MRSAS_MFI_FRAME_SIZE);
462272744Skadesai		cmd->frame->io.context = cmd->index;
463272744Skadesai		cmd->frame->io.pad_0 = 0;
464272744Skadesai	}
465265555Sambrisko
466272744Skadesai	return (0);
467265555Sambrisko}
468265555Sambrisko
469272744Skadesai/*
470272744Skadesai * mrsas_alloc_frame:	Allocates MFI Frames
471272744Skadesai * input:				Adapter soft state
472265555Sambrisko *
473272744Skadesai * Create bus DMA memory tag and dmamap and load memory for MFI frames. Returns
474272744Skadesai * virtual memory pointer to allocated region.
475265555Sambrisko */
476272744Skadesaivoid   *
477272744Skadesaimrsas_alloc_frame(struct mrsas_softc *sc, struct mrsas_mfi_cmd *cmd)
478265555Sambrisko{
479272744Skadesai	u_int32_t frame_size = MRSAS_MFI_FRAME_SIZE;
480265555Sambrisko
481272744Skadesai	if (bus_dmamem_alloc(sc->mficmd_frame_tag, (void **)&cmd->frame_mem,
482272744Skadesai	    BUS_DMA_NOWAIT, &cmd->frame_dmamap)) {
483272744Skadesai		device_printf(sc->mrsas_dev, "Cannot alloc MFI frame memory\n");
484272744Skadesai		return (NULL);
485272744Skadesai	}
486272744Skadesai	if (bus_dmamap_load(sc->mficmd_frame_tag, cmd->frame_dmamap,
487272744Skadesai	    cmd->frame_mem, frame_size, mrsas_alloc_cb,
488272744Skadesai	    &cmd->frame_phys_addr, BUS_DMA_NOWAIT)) {
489272744Skadesai		device_printf(sc->mrsas_dev, "Cannot load IO request memory\n");
490272744Skadesai		return (NULL);
491272744Skadesai	}
492272744Skadesai	return (cmd->frame_mem);
493265555Sambrisko}
494265555Sambrisko
495265555Sambrisko/*
496272744Skadesai * mrsas_alloc_cb:	Callback function of bus_dmamap_load()
497272744Skadesai * input:			callback argument,
498272744Skadesai * 					machine dependent type that describes DMA segments,
499272744Skadesai * 					number of segments,
500272744Skadesai * 					error code.
501265555Sambrisko *
502272744Skadesai * This function is for the driver to receive mapping information resultant of
503272744Skadesai * the bus_dmamap_load(). The information is actually not being used, but the
504272744Skadesai * address is saved anyway.
505265555Sambrisko */
506272744Skadesaistatic void
507272744Skadesaimrsas_alloc_cb(void *arg, bus_dma_segment_t *segs,
508272744Skadesai    int nsegs, int error)
509265555Sambrisko{
510272744Skadesai	bus_addr_t *addr;
511265555Sambrisko
512272744Skadesai	addr = arg;
513272744Skadesai	*addr = segs[0].ds_addr;
514265555Sambrisko}
515265555Sambrisko
516272744Skadesai/*
517272744Skadesai * mrsas_free_frames:	Frees memory for  MFI frames
518272744Skadesai * input:				Adapter soft state
519265555Sambrisko *
520272744Skadesai * Deallocates MFI frames memory.  Called from mrsas_free_mem() during detach
521272744Skadesai * and error case during creation of frame pool.
522265555Sambrisko */
523272744Skadesaivoid
524272744Skadesaimrsas_free_frame(struct mrsas_softc *sc, struct mrsas_mfi_cmd *cmd)
525265555Sambrisko{
526272744Skadesai	if (cmd->frame_phys_addr)
527272744Skadesai		bus_dmamap_unload(sc->mficmd_frame_tag, cmd->frame_dmamap);
528272744Skadesai	if (cmd->frame_mem != NULL)
529272744Skadesai		bus_dmamem_free(sc->mficmd_frame_tag, cmd->frame_mem, cmd->frame_dmamap);
530265555Sambrisko}
531