xref: /dflybsd-src/sys/dev/raid/mrsas/mrsas_ioctl.c (revision 030b0c8c4cf27c560ccec70410c8e21934ae677d)
16d743f04SSascha Wildner /*
26d743f04SSascha Wildner  * Copyright (c) 2014, LSI Corp.
36d743f04SSascha Wildner  * All rights reserved.
46d743f04SSascha Wildner  * Author: Marian Choy
56d743f04SSascha Wildner  * Support: freebsdraid@lsi.com
66d743f04SSascha Wildner  *
76d743f04SSascha Wildner  * Redistribution and use in source and binary forms, with or without
86d743f04SSascha Wildner  * modification, are permitted provided that the following conditions
96d743f04SSascha Wildner  * are met:
106d743f04SSascha Wildner  *
116d743f04SSascha Wildner  * 1. Redistributions of source code must retain the above copyright
126d743f04SSascha Wildner  *    notice, this list of conditions and the following disclaimer.
136d743f04SSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
146d743f04SSascha Wildner  *    notice, this list of conditions and the following disclaimer in
156d743f04SSascha Wildner  *    the documentation and/or other materials provided with the
166d743f04SSascha Wildner  *    distribution.
176d743f04SSascha Wildner  * 3. Neither the name of the <ORGANIZATION> nor the names of its
186d743f04SSascha Wildner  *    contributors may be used to endorse or promote products derived
196d743f04SSascha Wildner  *    from this software without specific prior written permission.
206d743f04SSascha Wildner  *
216d743f04SSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
226d743f04SSascha Wildner  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
236d743f04SSascha Wildner  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
246d743f04SSascha Wildner  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
256d743f04SSascha Wildner  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
266d743f04SSascha Wildner  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
276d743f04SSascha Wildner  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
286d743f04SSascha Wildner  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
296d743f04SSascha Wildner  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
306d743f04SSascha Wildner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
316d743f04SSascha Wildner  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
326d743f04SSascha Wildner  * POSSIBILITY OF SUCH DAMAGE.
336d743f04SSascha Wildner  *
346d743f04SSascha Wildner  * The views and conclusions contained in the software and documentation
356d743f04SSascha Wildner  * are those of the authors and should not be interpreted as representing
366d743f04SSascha Wildner  * official policies,either expressed or implied, of the FreeBSD Project.
376d743f04SSascha Wildner  *
386d743f04SSascha Wildner  * Send feedback to: <megaraidfbsd@lsi.com>
396d743f04SSascha Wildner  * Mail to: LSI Corporation, 1621 Barber Lane, Milpitas, CA 95035
406d743f04SSascha Wildner  *    ATTN: MegaRaid FreeBSD
416d743f04SSascha Wildner  *
426d743f04SSascha Wildner  * $FreeBSD: head/sys/dev/mrsas/mrsas_ioctl.c 265555 2014-05-07 16:16:49Z ambrisko $
436d743f04SSascha Wildner  */
446d743f04SSascha Wildner 
456d743f04SSascha Wildner #include <dev/raid/mrsas/mrsas.h>
466d743f04SSascha Wildner #include <dev/raid/mrsas/mrsas_ioctl.h>
476d743f04SSascha Wildner 
486d743f04SSascha Wildner /*
496d743f04SSascha Wildner  * Function prototypes
506d743f04SSascha Wildner  */
516d743f04SSascha Wildner int mrsas_alloc_mfi_cmds(struct mrsas_softc *sc);
526d743f04SSascha Wildner int mrsas_passthru(struct mrsas_softc *sc, void *arg);
536d743f04SSascha Wildner void mrsas_free_ioc_cmd(struct mrsas_softc *sc);
546d743f04SSascha Wildner void mrsas_free_frame(struct mrsas_softc *sc, struct mrsas_mfi_cmd *cmd);
556d743f04SSascha Wildner void mrsas_dump_dcmd(struct mrsas_softc *sc, struct mrsas_dcmd_frame* dcmd);
566d743f04SSascha Wildner void mrsas_dump_ioctl(struct mrsas_softc *sc, struct mrsas_iocpacket *user_ioc);
576d743f04SSascha Wildner void * mrsas_alloc_frame(struct mrsas_softc *sc, struct mrsas_mfi_cmd *cmd);
586d743f04SSascha Wildner static int mrsas_create_frame_pool(struct mrsas_softc *sc);
596d743f04SSascha Wildner static void mrsas_alloc_cb(void *arg, bus_dma_segment_t *segs,
606d743f04SSascha Wildner         int nsegs, int error);
616d743f04SSascha Wildner 
626d743f04SSascha Wildner extern struct mrsas_mfi_cmd* mrsas_get_mfi_cmd(struct mrsas_softc *sc);
636d743f04SSascha Wildner extern void mrsas_release_mfi_cmd(struct mrsas_mfi_cmd *cmd);
646d743f04SSascha Wildner extern int mrsas_issue_blocked_cmd(struct mrsas_softc *sc,
656d743f04SSascha Wildner     struct mrsas_mfi_cmd *cmd);
666d743f04SSascha Wildner 
676d743f04SSascha Wildner 
686d743f04SSascha Wildner /**
696d743f04SSascha Wildner  * mrsas_dump_ioctl:       Print debug output for DCMDs
706d743f04SSascha Wildner  * input:                  Adapter instance soft state
716d743f04SSascha Wildner  *                         DCMD frame structure
726d743f04SSascha Wildner  *
736d743f04SSascha Wildner  * This function is called from mrsas_passthru() to print out debug information
746d743f04SSascha Wildner  * in the handling and routing of DCMD commands.
756d743f04SSascha Wildner  */
mrsas_dump_dcmd(struct mrsas_softc * sc,struct mrsas_dcmd_frame * dcmd)766d743f04SSascha Wildner void mrsas_dump_dcmd( struct mrsas_softc *sc, struct mrsas_dcmd_frame* dcmd )
776d743f04SSascha Wildner {
786d743f04SSascha Wildner     int i;
796d743f04SSascha Wildner 
806d743f04SSascha Wildner     device_printf(sc->mrsas_dev, "dcmd->cmd:           0x%02hhx\n", dcmd->cmd);
816d743f04SSascha Wildner     device_printf(sc->mrsas_dev, "dcmd->cmd_status:    0x%02hhx\n", dcmd->cmd_status);
826d743f04SSascha Wildner     device_printf(sc->mrsas_dev, "dcmd->sge_count:     0x%02hhx\n", dcmd->sge_count);
836d743f04SSascha Wildner     device_printf(sc->mrsas_dev, "dcmd->context:       0x%08x\n", dcmd->context);
846d743f04SSascha Wildner     device_printf(sc->mrsas_dev, "dcmd->flags:         0x%04hx\n", dcmd->flags);
856d743f04SSascha Wildner     device_printf(sc->mrsas_dev, "dcmd->timeout:       0x%04hx\n", dcmd->timeout);
866d743f04SSascha Wildner     device_printf(sc->mrsas_dev, "dcmd->data_xfer_len: 0x%08x\n", dcmd->data_xfer_len);
876d743f04SSascha Wildner     device_printf(sc->mrsas_dev, "dcmd->opcode:        0x%08x\n", dcmd->opcode);
886d743f04SSascha Wildner     device_printf(sc->mrsas_dev, "dcmd->mbox.w[0]:     0x%08x\n", dcmd->mbox.w[0]);
896d743f04SSascha Wildner     device_printf(sc->mrsas_dev, "dcmd->mbox.w[1]:     0x%08x\n", dcmd->mbox.w[1]);
906d743f04SSascha Wildner     device_printf(sc->mrsas_dev, "dcmd->mbox.w[2]:     0x%08x\n", dcmd->mbox.w[2]);
916d743f04SSascha Wildner     for (i=0; i< MIN(MAX_IOCTL_SGE, dcmd->sge_count); i++) {
926d743f04SSascha Wildner         device_printf(sc->mrsas_dev, "sgl[%02d]\n", i);
936d743f04SSascha Wildner         device_printf(sc->mrsas_dev, "    sge32[%02d].phys_addr: 0x%08x\n",
946d743f04SSascha Wildner             i, dcmd->sgl.sge32[i].phys_addr);
956d743f04SSascha Wildner         device_printf(sc->mrsas_dev, "    sge32[%02d].length:    0x%08x\n",
966d743f04SSascha Wildner             i, dcmd->sgl.sge32[i].length);
976d743f04SSascha Wildner         device_printf(sc->mrsas_dev, "    sge64[%02d].phys_addr: 0x%08llx\n",
988fb66b81SSascha Wildner             i, (unsigned long long) dcmd->sgl.sge64[i].phys_addr);
996d743f04SSascha Wildner         device_printf(sc->mrsas_dev, "    sge64[%02d].length:    0x%08x\n",
1006d743f04SSascha Wildner             i, dcmd->sgl.sge64[i].length);
1016d743f04SSascha Wildner     }
1026d743f04SSascha Wildner }
1036d743f04SSascha Wildner 
1046d743f04SSascha Wildner /**
1056d743f04SSascha Wildner  * mrsas_dump_ioctl:       Print debug output for ioctl
1066d743f04SSascha Wildner  * input:                  Adapter instance soft state
1076d743f04SSascha Wildner  *                         iocpacket structure
1086d743f04SSascha Wildner  *
1096d743f04SSascha Wildner  * This function is called from mrsas_passthru() to print out debug information
1106d743f04SSascha Wildner  * in the handling and routing of ioctl commands.
1116d743f04SSascha Wildner  */
mrsas_dump_ioctl(struct mrsas_softc * sc,struct mrsas_iocpacket * user_ioc)1126d743f04SSascha Wildner void mrsas_dump_ioctl(struct mrsas_softc *sc, struct mrsas_iocpacket *user_ioc)
1136d743f04SSascha Wildner {
1146d743f04SSascha Wildner     union mrsas_frame *in_cmd = (union mrsas_frame *) &(user_ioc->frame.raw);
1156d743f04SSascha Wildner     struct mrsas_dcmd_frame* dcmd = (struct mrsas_dcmd_frame *) &(in_cmd->dcmd);
1166d743f04SSascha Wildner     int i;
1176d743f04SSascha Wildner 
1186d743f04SSascha Wildner     device_printf(sc->mrsas_dev,
1196d743f04SSascha Wildner         "====== In %s() ======================================\n", __func__);
1206d743f04SSascha Wildner     device_printf(sc->mrsas_dev, "host_no:    0x%04hx\n", user_ioc->host_no);
1216d743f04SSascha Wildner     device_printf(sc->mrsas_dev, " __pad1:    0x%04hx\n", user_ioc->__pad1);
1226d743f04SSascha Wildner     device_printf(sc->mrsas_dev, "sgl_off:    0x%08x\n",  user_ioc->sgl_off);
1236d743f04SSascha Wildner     device_printf(sc->mrsas_dev, "sge_count:  0x%08x\n",  user_ioc->sge_count);
1246d743f04SSascha Wildner     device_printf(sc->mrsas_dev, "sense_off:  0x%08x\n",  user_ioc->sense_off);
1256d743f04SSascha Wildner     device_printf(sc->mrsas_dev, "sense_len:  0x%08x\n",  user_ioc->sense_len);
1266d743f04SSascha Wildner 
1276d743f04SSascha Wildner     mrsas_dump_dcmd(sc, dcmd);
1286d743f04SSascha Wildner 
1296d743f04SSascha Wildner     for (i=0; i< MIN(MAX_IOCTL_SGE, user_ioc->sge_count); i++) {
1306d743f04SSascha Wildner         device_printf(sc->mrsas_dev, "sge[%02d]\n", i);
1316d743f04SSascha Wildner         device_printf(sc->mrsas_dev,
1326d743f04SSascha Wildner             "    iov_base: %p\n", user_ioc->sgl[i].iov_base);
1336d743f04SSascha Wildner         device_printf(sc->mrsas_dev, "    iov_len:  %p\n",
1346d743f04SSascha Wildner             (void*)user_ioc->sgl[i].iov_len);
1356d743f04SSascha Wildner     }
1366d743f04SSascha Wildner     device_printf(sc->mrsas_dev,
1376d743f04SSascha Wildner         "==================================================================\n");
1386d743f04SSascha Wildner }
1396d743f04SSascha Wildner 
1406d743f04SSascha Wildner /**
1416d743f04SSascha Wildner  * mrsas_passthru:        Handle pass-through commands
1426d743f04SSascha Wildner  * input:                 Adapter instance soft state
1436d743f04SSascha Wildner  *                        argument pointer
1446d743f04SSascha Wildner  *
1456d743f04SSascha Wildner  * This function is called from mrsas_ioctl() to handle pass-through and
1466d743f04SSascha Wildner  * ioctl commands to Firmware.
1476d743f04SSascha Wildner  */
mrsas_passthru(struct mrsas_softc * sc,void * arg)1486d743f04SSascha Wildner int mrsas_passthru( struct mrsas_softc *sc, void *arg )
1496d743f04SSascha Wildner {
1506d743f04SSascha Wildner     struct mrsas_iocpacket *user_ioc = (struct mrsas_iocpacket *)arg;
1516d743f04SSascha Wildner     union  mrsas_frame *in_cmd = (union mrsas_frame *) &(user_ioc->frame.raw);
1526d743f04SSascha Wildner     struct mrsas_mfi_cmd *cmd = NULL;
1536d743f04SSascha Wildner     bus_dma_tag_t ioctl_data_tag[MAX_IOCTL_SGE];
1546d743f04SSascha Wildner     bus_dmamap_t ioctl_data_dmamap[MAX_IOCTL_SGE];
1556d743f04SSascha Wildner     void *ioctl_data_mem[MAX_IOCTL_SGE];  // ioctl data virtual addr
1566d743f04SSascha Wildner     bus_addr_t ioctl_data_phys_addr[MAX_IOCTL_SGE]; // ioctl data phys addr
1576d743f04SSascha Wildner     bus_dma_tag_t ioctl_sense_tag = 0;
1586d743f04SSascha Wildner     bus_dmamap_t ioctl_sense_dmamap = 0;
159394b58a3SSascha Wildner     void *ioctl_sense_mem = NULL;
1606d743f04SSascha Wildner     bus_addr_t ioctl_sense_phys_addr = 0;
1616d743f04SSascha Wildner     int i, adapter, ioctl_data_size, ioctl_sense_size, ret=0;
1626d743f04SSascha Wildner     struct mrsas_sge32 *kern_sge32;
1636d743f04SSascha Wildner     unsigned long *sense_ptr;
1646d743f04SSascha Wildner 
1656d743f04SSascha Wildner     /* For debug - uncomment the following line for debug output */
1666d743f04SSascha Wildner     //mrsas_dump_ioctl(sc, user_ioc);
1676d743f04SSascha Wildner 
1686d743f04SSascha Wildner     /*
1696d743f04SSascha Wildner      * Check for NOP from MegaCli... MegaCli can issue a DCMD of 0.  In this
1706d743f04SSascha Wildner      * case do nothing and return 0 to it as status.
1716d743f04SSascha Wildner      */
1726d743f04SSascha Wildner     if (in_cmd->dcmd.opcode == 0) {
1736d743f04SSascha Wildner         device_printf(sc->mrsas_dev, "In %s() Got a NOP\n", __func__);
1746d743f04SSascha Wildner         user_ioc->frame.hdr.cmd_status = MFI_STAT_OK;
1756d743f04SSascha Wildner         return (0);
1766d743f04SSascha Wildner     }
1776d743f04SSascha Wildner 
1786d743f04SSascha Wildner     /* Validate host_no */
1796d743f04SSascha Wildner     adapter = user_ioc->host_no;
1806d743f04SSascha Wildner     if (adapter != device_get_unit(sc->mrsas_dev)) {
1816d743f04SSascha Wildner         device_printf(sc->mrsas_dev, "In %s() IOCTL not for me!\n", __func__);
1826d743f04SSascha Wildner         return(ENOENT);
1836d743f04SSascha Wildner     }
1846d743f04SSascha Wildner 
1856d743f04SSascha Wildner     /* Validate SGL length */
1866d743f04SSascha Wildner     if (user_ioc->sge_count > MAX_IOCTL_SGE) {
1876d743f04SSascha Wildner         device_printf(sc->mrsas_dev, "In %s() SGL is too long (%d > 8).\n",
1886d743f04SSascha Wildner             __func__, user_ioc->sge_count);
1896d743f04SSascha Wildner         return(ENOENT);
1906d743f04SSascha Wildner     }
1916d743f04SSascha Wildner 
1926d743f04SSascha Wildner     /* Get a command */
1936d743f04SSascha Wildner     cmd = mrsas_get_mfi_cmd(sc);
1946d743f04SSascha Wildner     if (!cmd) {
1956d743f04SSascha Wildner         device_printf(sc->mrsas_dev, "Failed to get a free cmd for IOCTL\n");
1966d743f04SSascha Wildner         return(ENOMEM);
1976d743f04SSascha Wildner     }
1986d743f04SSascha Wildner 
1996d743f04SSascha Wildner     /*
2006d743f04SSascha Wildner      * User's IOCTL packet has 2 frames (maximum). Copy those two
2016d743f04SSascha Wildner      * frames into our cmd's frames. cmd->frame's context will get
2026d743f04SSascha Wildner      * overwritten when we copy from user's frames. So set that value
2036d743f04SSascha Wildner      * alone separately
2046d743f04SSascha Wildner      */
2056d743f04SSascha Wildner     memcpy(cmd->frame, user_ioc->frame.raw, 2 * MEGAMFI_FRAME_SIZE);
2066d743f04SSascha Wildner     cmd->frame->hdr.context = cmd->index;
2076d743f04SSascha Wildner     cmd->frame->hdr.pad_0 = 0;
2086d743f04SSascha Wildner     cmd->frame->hdr.flags &= ~(MFI_FRAME_IEEE | MFI_FRAME_SGL64 |
2096d743f04SSascha Wildner                                MFI_FRAME_SENSE64);
2106d743f04SSascha Wildner 
2116d743f04SSascha Wildner     /*
2126d743f04SSascha Wildner      * The management interface between applications and the fw uses
2136d743f04SSascha Wildner      * MFI frames. E.g, RAID configuration changes, LD property changes
2146d743f04SSascha Wildner      * etc are accomplishes through different kinds of MFI frames. The
2156d743f04SSascha Wildner      * driver needs to care only about substituting user buffers with
2166d743f04SSascha Wildner      * kernel buffers in SGLs. The location of SGL is embedded in the
2176d743f04SSascha Wildner      * struct iocpacket itself.
2186d743f04SSascha Wildner      */
2196d743f04SSascha Wildner     kern_sge32 = (struct mrsas_sge32 *)
2206d743f04SSascha Wildner         ((unsigned long)cmd->frame + user_ioc->sgl_off);
2216d743f04SSascha Wildner 
2226d743f04SSascha Wildner     /*
2236d743f04SSascha Wildner      * For each user buffer, create a mirror buffer and copy in
2246d743f04SSascha Wildner      */
2256d743f04SSascha Wildner     for (i=0; i < user_ioc->sge_count; i++) {
2266d743f04SSascha Wildner         if (!user_ioc->sgl[i].iov_len)
2276d743f04SSascha Wildner             continue;
2286d743f04SSascha Wildner         ioctl_data_size = user_ioc->sgl[i].iov_len;
2296d743f04SSascha Wildner         if (bus_dma_tag_create( sc->mrsas_parent_tag,   // parent
2306d743f04SSascha Wildner                                 1, 0,                   // algnmnt, boundary
2316d743f04SSascha Wildner                                 BUS_SPACE_MAXADDR_32BIT,// lowaddr
2326d743f04SSascha Wildner                                 BUS_SPACE_MAXADDR,      // highaddr
2336d743f04SSascha Wildner                                 ioctl_data_size,        // maxsize
2346d743f04SSascha Wildner                                 1,                      // msegments
2356d743f04SSascha Wildner                                 ioctl_data_size,        // maxsegsize
2366d743f04SSascha Wildner                                 BUS_DMA_ALLOCNOW,       // flags
2376d743f04SSascha Wildner                                 &ioctl_data_tag[i])) {
2386d743f04SSascha Wildner             device_printf(sc->mrsas_dev, "Cannot allocate ioctl data tag\n");
2396d743f04SSascha Wildner             return (ENOMEM);
2406d743f04SSascha Wildner         }
2416d743f04SSascha Wildner         if (bus_dmamem_alloc(ioctl_data_tag[i], (void **)&ioctl_data_mem[i],
2426d743f04SSascha Wildner                 (BUS_DMA_NOWAIT | BUS_DMA_ZERO), &ioctl_data_dmamap[i])) {
2436d743f04SSascha Wildner             device_printf(sc->mrsas_dev, "Cannot allocate ioctl data mem\n");
2446d743f04SSascha Wildner             return (ENOMEM);
2456d743f04SSascha Wildner         }
2466d743f04SSascha Wildner         if (bus_dmamap_load(ioctl_data_tag[i], ioctl_data_dmamap[i],
2476d743f04SSascha Wildner                 ioctl_data_mem[i], ioctl_data_size, mrsas_alloc_cb,
2486d743f04SSascha Wildner                 &ioctl_data_phys_addr[i], BUS_DMA_NOWAIT)) {
2496d743f04SSascha Wildner             device_printf(sc->mrsas_dev, "Cannot load ioctl data mem\n");
2506d743f04SSascha Wildner             return (ENOMEM);
2516d743f04SSascha Wildner         }
2526d743f04SSascha Wildner 
2536d743f04SSascha Wildner         /* Save the physical address and length */
2546d743f04SSascha Wildner         kern_sge32[i].phys_addr = (u_int32_t)ioctl_data_phys_addr[i];
2556d743f04SSascha Wildner         kern_sge32[i].length = user_ioc->sgl[i].iov_len;
2566d743f04SSascha Wildner 
2576d743f04SSascha Wildner         /* Copy in data from user space */
2586d743f04SSascha Wildner         ret = copyin(user_ioc->sgl[i].iov_base, ioctl_data_mem[i],
2596d743f04SSascha Wildner                         user_ioc->sgl[i].iov_len);
2606d743f04SSascha Wildner         if (ret) {
2616d743f04SSascha Wildner             device_printf(sc->mrsas_dev, "IOCTL copyin failed!\n");
2626d743f04SSascha Wildner             goto out;
2636d743f04SSascha Wildner         }
2646d743f04SSascha Wildner     }
2656d743f04SSascha Wildner 
2666d743f04SSascha Wildner     ioctl_sense_size = user_ioc->sense_len;
2676d743f04SSascha Wildner     if (user_ioc->sense_len) {
2686d743f04SSascha Wildner         if (bus_dma_tag_create( sc->mrsas_parent_tag,   // parent
2696d743f04SSascha Wildner                                 1, 0,                   // algnmnt, boundary
2706d743f04SSascha Wildner                                 BUS_SPACE_MAXADDR_32BIT,// lowaddr
2716d743f04SSascha Wildner                                 BUS_SPACE_MAXADDR,      // highaddr
2726d743f04SSascha Wildner                                 ioctl_sense_size,       // maxsize
2736d743f04SSascha Wildner                                 1,                      // msegments
2746d743f04SSascha Wildner                                 ioctl_sense_size,       // maxsegsize
2756d743f04SSascha Wildner                                 BUS_DMA_ALLOCNOW,       // flags
2766d743f04SSascha Wildner                                 &ioctl_sense_tag)) {
2776d743f04SSascha Wildner             device_printf(sc->mrsas_dev, "Cannot allocate ioctl sense tag\n");
2786d743f04SSascha Wildner             return (ENOMEM);
2796d743f04SSascha Wildner         }
2806d743f04SSascha Wildner         if (bus_dmamem_alloc(ioctl_sense_tag, (void **)&ioctl_sense_mem,
2816d743f04SSascha Wildner                 (BUS_DMA_NOWAIT | BUS_DMA_ZERO), &ioctl_sense_dmamap)) {
2826d743f04SSascha Wildner             device_printf(sc->mrsas_dev, "Cannot allocate ioctl data mem\n");
2836d743f04SSascha Wildner             return (ENOMEM);
2846d743f04SSascha Wildner         }
2856d743f04SSascha Wildner         if (bus_dmamap_load(ioctl_sense_tag, ioctl_sense_dmamap,
2866d743f04SSascha Wildner                 ioctl_sense_mem, ioctl_sense_size, mrsas_alloc_cb,
2876d743f04SSascha Wildner                 &ioctl_sense_phys_addr, BUS_DMA_NOWAIT)) {
2886d743f04SSascha Wildner             device_printf(sc->mrsas_dev, "Cannot load ioctl sense mem\n");
2896d743f04SSascha Wildner             return (ENOMEM);
2906d743f04SSascha Wildner         }
2916d743f04SSascha Wildner         sense_ptr =
2926d743f04SSascha Wildner             (unsigned long *)((unsigned long)cmd->frame + user_ioc->sense_off);
293*499494d8SSascha Wildner 	*sense_ptr = ioctl_sense_phys_addr;
2946d743f04SSascha Wildner     }
2956d743f04SSascha Wildner 
2966d743f04SSascha Wildner     /*
2976d743f04SSascha Wildner      * Set the sync_cmd flag so that the ISR knows not to complete this
2986d743f04SSascha Wildner      * cmd to the SCSI mid-layer
2996d743f04SSascha Wildner      */
3006d743f04SSascha Wildner     cmd->sync_cmd = 1;
3016d743f04SSascha Wildner     mrsas_issue_blocked_cmd(sc, cmd);
3026d743f04SSascha Wildner     cmd->sync_cmd = 0;
3036d743f04SSascha Wildner 
3046d743f04SSascha Wildner     /*
3056d743f04SSascha Wildner      * copy out the kernel buffers to user buffers
3066d743f04SSascha Wildner      */
3076d743f04SSascha Wildner     for (i = 0; i < user_ioc->sge_count; i++) {
3086d743f04SSascha Wildner         ret = copyout(ioctl_data_mem[i], user_ioc->sgl[i].iov_base,
3096d743f04SSascha Wildner             user_ioc->sgl[i].iov_len);
3106d743f04SSascha Wildner         if (ret) {
3116d743f04SSascha Wildner             device_printf(sc->mrsas_dev, "IOCTL copyout failed!\n");
3126d743f04SSascha Wildner             goto out;
3136d743f04SSascha Wildner         }
3146d743f04SSascha Wildner     }
3156d743f04SSascha Wildner 
3166d743f04SSascha Wildner     /*
3176d743f04SSascha Wildner      * copy out the sense
3186d743f04SSascha Wildner      */
3196d743f04SSascha Wildner     if (user_ioc->sense_len) {
3206d743f04SSascha Wildner         /*
3216d743f04SSascha Wildner          * sense_buff points to the location that has the user
3226d743f04SSascha Wildner          * sense buffer address
3236d743f04SSascha Wildner          */
3246d743f04SSascha Wildner         sense_ptr = (unsigned long *) ((unsigned long)user_ioc->frame.raw +
3256d743f04SSascha Wildner                       user_ioc->sense_off);
3266d743f04SSascha Wildner         ret = copyout(ioctl_sense_mem, (unsigned long*)*sense_ptr,
3276d743f04SSascha Wildner                       user_ioc->sense_len);
3286d743f04SSascha Wildner         if (ret) {
3296d743f04SSascha Wildner             device_printf(sc->mrsas_dev, "IOCTL sense copyout failed!\n");
3306d743f04SSascha Wildner             goto out;
3316d743f04SSascha Wildner         }
3326d743f04SSascha Wildner     }
3336d743f04SSascha Wildner 
3346d743f04SSascha Wildner     /*
3356d743f04SSascha Wildner      * Return command status to user space
3366d743f04SSascha Wildner      */
3376d743f04SSascha Wildner     memcpy(&user_ioc->frame.hdr.cmd_status, &cmd->frame->hdr.cmd_status,
3386d743f04SSascha Wildner             sizeof(u_int8_t));
3396d743f04SSascha Wildner 
3406d743f04SSascha Wildner out:
3416d743f04SSascha Wildner     /*
3426d743f04SSascha Wildner      * Release sense buffer
3436d743f04SSascha Wildner      */
3446d743f04SSascha Wildner     if (ioctl_sense_phys_addr)
3456d743f04SSascha Wildner         bus_dmamap_unload(ioctl_sense_tag, ioctl_sense_dmamap);
3466d743f04SSascha Wildner     if (ioctl_sense_mem)
3476d743f04SSascha Wildner         bus_dmamem_free(ioctl_sense_tag, ioctl_sense_mem, ioctl_sense_dmamap);
3486d743f04SSascha Wildner     if (ioctl_sense_tag)
3496d743f04SSascha Wildner         bus_dma_tag_destroy(ioctl_sense_tag);
3506d743f04SSascha Wildner 
3516d743f04SSascha Wildner     /*
3526d743f04SSascha Wildner      * Release data buffers
3536d743f04SSascha Wildner      */
3546d743f04SSascha Wildner     for (i = 0; i < user_ioc->sge_count; i++) {
3556d743f04SSascha Wildner         if (!user_ioc->sgl[i].iov_len)
3566d743f04SSascha Wildner             continue;
3576d743f04SSascha Wildner         if (ioctl_data_phys_addr[i])
3586d743f04SSascha Wildner             bus_dmamap_unload(ioctl_data_tag[i], ioctl_data_dmamap[i]);
3596d743f04SSascha Wildner         if (ioctl_data_mem[i] != NULL)
3606d743f04SSascha Wildner             bus_dmamem_free(ioctl_data_tag[i], ioctl_data_mem[i],
3616d743f04SSascha Wildner                 ioctl_data_dmamap[i]);
3626d743f04SSascha Wildner         if (ioctl_data_tag[i] != NULL)
3636d743f04SSascha Wildner             bus_dma_tag_destroy(ioctl_data_tag[i]);
3646d743f04SSascha Wildner     }
3656d743f04SSascha Wildner 
3666d743f04SSascha Wildner     /* Free command */
3676d743f04SSascha Wildner     mrsas_release_mfi_cmd(cmd);
3686d743f04SSascha Wildner 
3696d743f04SSascha Wildner     return(ret);
3706d743f04SSascha Wildner }
3716d743f04SSascha Wildner 
3726d743f04SSascha Wildner /**
3736d743f04SSascha Wildner  * mrsas_alloc_mfi_cmds:  Allocates the command packets
3746d743f04SSascha Wildner  * input:                 Adapter instance soft state
3756d743f04SSascha Wildner  *
3766d743f04SSascha Wildner  * Each IOCTL or passthru command that is issued to the FW are wrapped in a
3776d743f04SSascha Wildner  * local data structure called mrsas_mfi_cmd.  The frame embedded in this
3786d743f04SSascha Wildner  * mrsas_mfi is issued to FW. The array is used only to look up the
3796d743f04SSascha Wildner  * mrsas_mfi_cmd given the context. The free commands are maintained in a
3806d743f04SSascha Wildner  * linked list.
3816d743f04SSascha Wildner  */
mrsas_alloc_mfi_cmds(struct mrsas_softc * sc)3826d743f04SSascha Wildner int mrsas_alloc_mfi_cmds(struct mrsas_softc *sc)
3836d743f04SSascha Wildner {
3846d743f04SSascha Wildner     int i, j;
3856d743f04SSascha Wildner     u_int32_t max_cmd;
3866d743f04SSascha Wildner     struct mrsas_mfi_cmd *cmd;
3876d743f04SSascha Wildner 
3886d743f04SSascha Wildner     max_cmd = MRSAS_MAX_MFI_CMDS;
3896d743f04SSascha Wildner 
3906d743f04SSascha Wildner     /*
3916d743f04SSascha Wildner      * sc->mfi_cmd_list is an array of struct mrsas_mfi_cmd pointers. Allocate the
3926d743f04SSascha Wildner      * dynamic array first and then allocate individual commands.
3936d743f04SSascha Wildner      */
3946d743f04SSascha Wildner     sc->mfi_cmd_list = kmalloc(sizeof(struct mrsas_mfi_cmd*)*max_cmd, M_MRSAS, M_NOWAIT);
3956d743f04SSascha Wildner     if (!sc->mfi_cmd_list) {
3966d743f04SSascha Wildner         device_printf(sc->mrsas_dev, "Cannot alloc memory for mfi_cmd cmd_list.\n");
3976d743f04SSascha Wildner         return(ENOMEM);
3986d743f04SSascha Wildner     }
3996d743f04SSascha Wildner     memset(sc->mfi_cmd_list, 0, sizeof(struct mrsas_mfi_cmd *)*max_cmd);
4006d743f04SSascha Wildner     for (i = 0; i < max_cmd; i++) {
4016d743f04SSascha Wildner         sc->mfi_cmd_list[i] = kmalloc(sizeof(struct mrsas_mfi_cmd),
4026d743f04SSascha Wildner                                  M_MRSAS, M_NOWAIT);
4036d743f04SSascha Wildner         if (!sc->mfi_cmd_list[i]) {
4046d743f04SSascha Wildner             for (j = 0; j < i; j++)
4056d743f04SSascha Wildner                 kfree(sc->mfi_cmd_list[j],M_MRSAS);
4066d743f04SSascha Wildner             kfree(sc->mfi_cmd_list, M_MRSAS);
4076d743f04SSascha Wildner             sc->mfi_cmd_list = NULL;
4086d743f04SSascha Wildner             return(ENOMEM);
4096d743f04SSascha Wildner         }
4106d743f04SSascha Wildner     }
4116d743f04SSascha Wildner 
4126d743f04SSascha Wildner     for (i = 0; i < max_cmd; i++) {
4136d743f04SSascha Wildner         cmd = sc->mfi_cmd_list[i];
4146d743f04SSascha Wildner         memset(cmd, 0, sizeof(struct mrsas_mfi_cmd));
4156d743f04SSascha Wildner         cmd->index = i;
4166d743f04SSascha Wildner         cmd->ccb_ptr = NULL;
4176d743f04SSascha Wildner         cmd->sc = sc;
4186d743f04SSascha Wildner         TAILQ_INSERT_TAIL(&(sc->mrsas_mfi_cmd_list_head), cmd, next);
4196d743f04SSascha Wildner     }
4206d743f04SSascha Wildner 
4216d743f04SSascha Wildner     /* create a frame pool and assign one frame to each command */
4226d743f04SSascha Wildner     if (mrsas_create_frame_pool(sc)) {
4236d743f04SSascha Wildner         device_printf(sc->mrsas_dev, "Cannot allocate DMA frame pool.\n");
4246d743f04SSascha Wildner         for (i = 0; i < MRSAS_MAX_MFI_CMDS; i++) { // Free the frames
4256d743f04SSascha Wildner             cmd = sc->mfi_cmd_list[i];
4266d743f04SSascha Wildner             mrsas_free_frame(sc, cmd);
4276d743f04SSascha Wildner         }
4286d743f04SSascha Wildner         if (sc->mficmd_frame_tag != NULL)
4296d743f04SSascha Wildner             bus_dma_tag_destroy(sc->mficmd_frame_tag);
4306d743f04SSascha Wildner         return(ENOMEM);
4316d743f04SSascha Wildner     }
4326d743f04SSascha Wildner 
4336d743f04SSascha Wildner     return(0);
4346d743f04SSascha Wildner }
4356d743f04SSascha Wildner 
4366d743f04SSascha Wildner /**
4376d743f04SSascha Wildner  * mrsas_create_frame_pool -   Creates DMA pool for cmd frames
4386d743f04SSascha Wildner  * input:                      Adapter soft state
4396d743f04SSascha Wildner  *
4406d743f04SSascha Wildner  * Each command packet has an embedded DMA memory buffer that is used for
4416d743f04SSascha Wildner  * filling MFI frame and the SG list that immediately follows the frame. This
4426d743f04SSascha Wildner  * function creates those DMA memory buffers for each command packet by using
4436d743f04SSascha Wildner  * PCI pool facility. pad_0 is initialized to 0 to prevent corrupting value
4446d743f04SSascha Wildner  * of context and could cause FW crash.
4456d743f04SSascha Wildner  */
mrsas_create_frame_pool(struct mrsas_softc * sc)4466d743f04SSascha Wildner static int mrsas_create_frame_pool(struct mrsas_softc *sc)
4476d743f04SSascha Wildner {
4486d743f04SSascha Wildner     int i;
4496d743f04SSascha Wildner     struct mrsas_mfi_cmd *cmd;
4506d743f04SSascha Wildner 
4516d743f04SSascha Wildner     if (bus_dma_tag_create( sc->mrsas_parent_tag,   // parent
4526d743f04SSascha Wildner                             1, 0,                   // algnmnt, boundary
4536d743f04SSascha Wildner                             BUS_SPACE_MAXADDR_32BIT,// lowaddr
4546d743f04SSascha Wildner                             BUS_SPACE_MAXADDR,      // highaddr
4556d743f04SSascha Wildner                             MRSAS_MFI_FRAME_SIZE,   // maxsize
4566d743f04SSascha Wildner                             1,                      // msegments
4576d743f04SSascha Wildner                             MRSAS_MFI_FRAME_SIZE,   // maxsegsize
4586d743f04SSascha Wildner                             BUS_DMA_ALLOCNOW,       // flags
4596d743f04SSascha Wildner                             &sc->mficmd_frame_tag)) {
4606d743f04SSascha Wildner         device_printf(sc->mrsas_dev, "Cannot create MFI frame tag\n");
4616d743f04SSascha Wildner         return (ENOMEM);
4626d743f04SSascha Wildner     }
4636d743f04SSascha Wildner 
4646d743f04SSascha Wildner     for (i = 0; i < MRSAS_MAX_MFI_CMDS; i++) {
4656d743f04SSascha Wildner         cmd = sc->mfi_cmd_list[i];
4666d743f04SSascha Wildner         cmd->frame = mrsas_alloc_frame(sc, cmd);
4676d743f04SSascha Wildner         if (cmd->frame == NULL) {
4686d743f04SSascha Wildner             device_printf(sc->mrsas_dev, "Cannot alloc MFI frame memory\n");
4696d743f04SSascha Wildner             return (ENOMEM);
4706d743f04SSascha Wildner         }
4716d743f04SSascha Wildner         memset(cmd->frame, 0, MRSAS_MFI_FRAME_SIZE);
4726d743f04SSascha Wildner         cmd->frame->io.context = cmd->index;
4736d743f04SSascha Wildner         cmd->frame->io.pad_0 = 0;
4746d743f04SSascha Wildner     }
4756d743f04SSascha Wildner 
4766d743f04SSascha Wildner     return(0);
4776d743f04SSascha Wildner }
4786d743f04SSascha Wildner 
4796d743f04SSascha Wildner /**
4806d743f04SSascha Wildner  * mrsas_alloc_frame -   Allocates MFI Frames
4816d743f04SSascha Wildner  * input:                Adapter soft state
4826d743f04SSascha Wildner  *
4836d743f04SSascha Wildner  * Create bus DMA memory tag and dmamap and load memory for MFI frames.
4846d743f04SSascha Wildner  * Returns virtual memory pointer to allocated region.
4856d743f04SSascha Wildner  */
mrsas_alloc_frame(struct mrsas_softc * sc,struct mrsas_mfi_cmd * cmd)4866d743f04SSascha Wildner void *mrsas_alloc_frame(struct mrsas_softc *sc, struct mrsas_mfi_cmd *cmd)
4876d743f04SSascha Wildner {
4886d743f04SSascha Wildner     u_int32_t frame_size = MRSAS_MFI_FRAME_SIZE;
4896d743f04SSascha Wildner 
4906d743f04SSascha Wildner     if (bus_dmamem_alloc(sc->mficmd_frame_tag, (void **)&cmd->frame_mem,
4916d743f04SSascha Wildner                     BUS_DMA_NOWAIT, &cmd->frame_dmamap)) {
4926d743f04SSascha Wildner         device_printf(sc->mrsas_dev, "Cannot alloc MFI frame memory\n");
4936d743f04SSascha Wildner         return (NULL);
4946d743f04SSascha Wildner     }
4956d743f04SSascha Wildner     if (bus_dmamap_load(sc->mficmd_frame_tag, cmd->frame_dmamap,
4966d743f04SSascha Wildner                         cmd->frame_mem, frame_size, mrsas_alloc_cb,
4976d743f04SSascha Wildner                         &cmd->frame_phys_addr, BUS_DMA_NOWAIT)) {
4986d743f04SSascha Wildner         device_printf(sc->mrsas_dev, "Cannot load IO request memory\n");
4996d743f04SSascha Wildner         return (NULL);
5006d743f04SSascha Wildner     }
5016d743f04SSascha Wildner 
5026d743f04SSascha Wildner     return(cmd->frame_mem);
5036d743f04SSascha Wildner }
5046d743f04SSascha Wildner 
5056d743f04SSascha Wildner /*
5066d743f04SSascha Wildner  * mrsas_alloc_cb:  Callback function of bus_dmamap_load()
5076d743f04SSascha Wildner  * input:           callback argument,
5086d743f04SSascha Wildner  *                  machine dependent type that describes DMA segments,
5096d743f04SSascha Wildner  *                  number of segments,
5106d743f04SSascha Wildner  *                  error code.
5116d743f04SSascha Wildner  *
5126d743f04SSascha Wildner  * This function is for the driver to receive mapping information resultant
5136d743f04SSascha Wildner  * of the bus_dmamap_load(). The information is actually not being used,
5146d743f04SSascha Wildner  * but the address is saved anyway.
5156d743f04SSascha Wildner  */
mrsas_alloc_cb(void * arg,bus_dma_segment_t * segs,int nsegs,int error)5166d743f04SSascha Wildner static void mrsas_alloc_cb(void *arg, bus_dma_segment_t *segs,
5176d743f04SSascha Wildner         int nsegs, int error)
5186d743f04SSascha Wildner {
5196d743f04SSascha Wildner     bus_addr_t *addr;
5206d743f04SSascha Wildner 
5216d743f04SSascha Wildner     addr = arg;
5226d743f04SSascha Wildner     *addr = segs[0].ds_addr;
5236d743f04SSascha Wildner }
5246d743f04SSascha Wildner 
5256d743f04SSascha Wildner /**
5266d743f04SSascha Wildner  * mrsas_free_frames:    Frees memory for  MFI frames
5276d743f04SSascha Wildner  * input:                Adapter soft state
5286d743f04SSascha Wildner  *
5296d743f04SSascha Wildner  * Deallocates MFI frames memory.  Called from mrsas_free_mem() during
5306d743f04SSascha Wildner  * detach and error case during creation of frame pool.
5316d743f04SSascha Wildner  */
mrsas_free_frame(struct mrsas_softc * sc,struct mrsas_mfi_cmd * cmd)5326d743f04SSascha Wildner void mrsas_free_frame(struct mrsas_softc *sc, struct mrsas_mfi_cmd *cmd)
5336d743f04SSascha Wildner {
5346d743f04SSascha Wildner     if (cmd->frame_phys_addr)
5356d743f04SSascha Wildner         bus_dmamap_unload(sc->mficmd_frame_tag, cmd->frame_dmamap);
5366d743f04SSascha Wildner     if (cmd->frame_mem != NULL)
5376d743f04SSascha Wildner         bus_dmamem_free(sc->mficmd_frame_tag, cmd->frame_mem, cmd->frame_dmamap);
5386d743f04SSascha Wildner }
539