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 */ 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", 986d743f04SSascha Wildner i, (long long unsigned int) 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 */ 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 */ 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; 159*394b58a3SSascha 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 NULL, NULL, // filter, filterarg 2346d743f04SSascha Wildner ioctl_data_size, // maxsize 2356d743f04SSascha Wildner 1, // msegments 2366d743f04SSascha Wildner ioctl_data_size, // maxsegsize 2376d743f04SSascha Wildner BUS_DMA_ALLOCNOW, // flags 2386d743f04SSascha Wildner &ioctl_data_tag[i])) { 2396d743f04SSascha Wildner device_printf(sc->mrsas_dev, "Cannot allocate ioctl data tag\n"); 2406d743f04SSascha Wildner return (ENOMEM); 2416d743f04SSascha Wildner } 2426d743f04SSascha Wildner if (bus_dmamem_alloc(ioctl_data_tag[i], (void **)&ioctl_data_mem[i], 2436d743f04SSascha Wildner (BUS_DMA_NOWAIT | BUS_DMA_ZERO), &ioctl_data_dmamap[i])) { 2446d743f04SSascha Wildner device_printf(sc->mrsas_dev, "Cannot allocate ioctl data mem\n"); 2456d743f04SSascha Wildner return (ENOMEM); 2466d743f04SSascha Wildner } 2476d743f04SSascha Wildner if (bus_dmamap_load(ioctl_data_tag[i], ioctl_data_dmamap[i], 2486d743f04SSascha Wildner ioctl_data_mem[i], ioctl_data_size, mrsas_alloc_cb, 2496d743f04SSascha Wildner &ioctl_data_phys_addr[i], BUS_DMA_NOWAIT)) { 2506d743f04SSascha Wildner device_printf(sc->mrsas_dev, "Cannot load ioctl data mem\n"); 2516d743f04SSascha Wildner return (ENOMEM); 2526d743f04SSascha Wildner } 2536d743f04SSascha Wildner 2546d743f04SSascha Wildner /* Save the physical address and length */ 2556d743f04SSascha Wildner kern_sge32[i].phys_addr = (u_int32_t)ioctl_data_phys_addr[i]; 2566d743f04SSascha Wildner kern_sge32[i].length = user_ioc->sgl[i].iov_len; 2576d743f04SSascha Wildner 2586d743f04SSascha Wildner /* Copy in data from user space */ 2596d743f04SSascha Wildner ret = copyin(user_ioc->sgl[i].iov_base, ioctl_data_mem[i], 2606d743f04SSascha Wildner user_ioc->sgl[i].iov_len); 2616d743f04SSascha Wildner if (ret) { 2626d743f04SSascha Wildner device_printf(sc->mrsas_dev, "IOCTL copyin failed!\n"); 2636d743f04SSascha Wildner goto out; 2646d743f04SSascha Wildner } 2656d743f04SSascha Wildner } 2666d743f04SSascha Wildner 2676d743f04SSascha Wildner ioctl_sense_size = user_ioc->sense_len; 2686d743f04SSascha Wildner if (user_ioc->sense_len) { 2696d743f04SSascha Wildner if (bus_dma_tag_create( sc->mrsas_parent_tag, // parent 2706d743f04SSascha Wildner 1, 0, // algnmnt, boundary 2716d743f04SSascha Wildner BUS_SPACE_MAXADDR_32BIT,// lowaddr 2726d743f04SSascha Wildner BUS_SPACE_MAXADDR, // highaddr 2736d743f04SSascha Wildner NULL, NULL, // filter, filterarg 2746d743f04SSascha Wildner ioctl_sense_size, // maxsize 2756d743f04SSascha Wildner 1, // msegments 2766d743f04SSascha Wildner ioctl_sense_size, // maxsegsize 2776d743f04SSascha Wildner BUS_DMA_ALLOCNOW, // flags 2786d743f04SSascha Wildner &ioctl_sense_tag)) { 2796d743f04SSascha Wildner device_printf(sc->mrsas_dev, "Cannot allocate ioctl sense tag\n"); 2806d743f04SSascha Wildner return (ENOMEM); 2816d743f04SSascha Wildner } 2826d743f04SSascha Wildner if (bus_dmamem_alloc(ioctl_sense_tag, (void **)&ioctl_sense_mem, 2836d743f04SSascha Wildner (BUS_DMA_NOWAIT | BUS_DMA_ZERO), &ioctl_sense_dmamap)) { 2846d743f04SSascha Wildner device_printf(sc->mrsas_dev, "Cannot allocate ioctl data mem\n"); 2856d743f04SSascha Wildner return (ENOMEM); 2866d743f04SSascha Wildner } 2876d743f04SSascha Wildner if (bus_dmamap_load(ioctl_sense_tag, ioctl_sense_dmamap, 2886d743f04SSascha Wildner ioctl_sense_mem, ioctl_sense_size, mrsas_alloc_cb, 2896d743f04SSascha Wildner &ioctl_sense_phys_addr, BUS_DMA_NOWAIT)) { 2906d743f04SSascha Wildner device_printf(sc->mrsas_dev, "Cannot load ioctl sense mem\n"); 2916d743f04SSascha Wildner return (ENOMEM); 2926d743f04SSascha Wildner } 2936d743f04SSascha Wildner sense_ptr = 2946d743f04SSascha Wildner (unsigned long *)((unsigned long)cmd->frame + user_ioc->sense_off); 2956d743f04SSascha Wildner sense_ptr = ioctl_sense_mem; 2966d743f04SSascha Wildner } 2976d743f04SSascha Wildner 2986d743f04SSascha Wildner /* 2996d743f04SSascha Wildner * Set the sync_cmd flag so that the ISR knows not to complete this 3006d743f04SSascha Wildner * cmd to the SCSI mid-layer 3016d743f04SSascha Wildner */ 3026d743f04SSascha Wildner cmd->sync_cmd = 1; 3036d743f04SSascha Wildner mrsas_issue_blocked_cmd(sc, cmd); 3046d743f04SSascha Wildner cmd->sync_cmd = 0; 3056d743f04SSascha Wildner 3066d743f04SSascha Wildner /* 3076d743f04SSascha Wildner * copy out the kernel buffers to user buffers 3086d743f04SSascha Wildner */ 3096d743f04SSascha Wildner for (i = 0; i < user_ioc->sge_count; i++) { 3106d743f04SSascha Wildner ret = copyout(ioctl_data_mem[i], user_ioc->sgl[i].iov_base, 3116d743f04SSascha Wildner user_ioc->sgl[i].iov_len); 3126d743f04SSascha Wildner if (ret) { 3136d743f04SSascha Wildner device_printf(sc->mrsas_dev, "IOCTL copyout failed!\n"); 3146d743f04SSascha Wildner goto out; 3156d743f04SSascha Wildner } 3166d743f04SSascha Wildner } 3176d743f04SSascha Wildner 3186d743f04SSascha Wildner /* 3196d743f04SSascha Wildner * copy out the sense 3206d743f04SSascha Wildner */ 3216d743f04SSascha Wildner if (user_ioc->sense_len) { 3226d743f04SSascha Wildner /* 3236d743f04SSascha Wildner * sense_buff points to the location that has the user 3246d743f04SSascha Wildner * sense buffer address 3256d743f04SSascha Wildner */ 3266d743f04SSascha Wildner sense_ptr = (unsigned long *) ((unsigned long)user_ioc->frame.raw + 3276d743f04SSascha Wildner user_ioc->sense_off); 3286d743f04SSascha Wildner ret = copyout(ioctl_sense_mem, (unsigned long*)*sense_ptr, 3296d743f04SSascha Wildner user_ioc->sense_len); 3306d743f04SSascha Wildner if (ret) { 3316d743f04SSascha Wildner device_printf(sc->mrsas_dev, "IOCTL sense copyout failed!\n"); 3326d743f04SSascha Wildner goto out; 3336d743f04SSascha Wildner } 3346d743f04SSascha Wildner } 3356d743f04SSascha Wildner 3366d743f04SSascha Wildner /* 3376d743f04SSascha Wildner * Return command status to user space 3386d743f04SSascha Wildner */ 3396d743f04SSascha Wildner memcpy(&user_ioc->frame.hdr.cmd_status, &cmd->frame->hdr.cmd_status, 3406d743f04SSascha Wildner sizeof(u_int8_t)); 3416d743f04SSascha Wildner 3426d743f04SSascha Wildner out: 3436d743f04SSascha Wildner /* 3446d743f04SSascha Wildner * Release sense buffer 3456d743f04SSascha Wildner */ 3466d743f04SSascha Wildner if (ioctl_sense_phys_addr) 3476d743f04SSascha Wildner bus_dmamap_unload(ioctl_sense_tag, ioctl_sense_dmamap); 3486d743f04SSascha Wildner if (ioctl_sense_mem) 3496d743f04SSascha Wildner bus_dmamem_free(ioctl_sense_tag, ioctl_sense_mem, ioctl_sense_dmamap); 3506d743f04SSascha Wildner if (ioctl_sense_tag) 3516d743f04SSascha Wildner bus_dma_tag_destroy(ioctl_sense_tag); 3526d743f04SSascha Wildner 3536d743f04SSascha Wildner /* 3546d743f04SSascha Wildner * Release data buffers 3556d743f04SSascha Wildner */ 3566d743f04SSascha Wildner for (i = 0; i < user_ioc->sge_count; i++) { 3576d743f04SSascha Wildner if (!user_ioc->sgl[i].iov_len) 3586d743f04SSascha Wildner continue; 3596d743f04SSascha Wildner if (ioctl_data_phys_addr[i]) 3606d743f04SSascha Wildner bus_dmamap_unload(ioctl_data_tag[i], ioctl_data_dmamap[i]); 3616d743f04SSascha Wildner if (ioctl_data_mem[i] != NULL) 3626d743f04SSascha Wildner bus_dmamem_free(ioctl_data_tag[i], ioctl_data_mem[i], 3636d743f04SSascha Wildner ioctl_data_dmamap[i]); 3646d743f04SSascha Wildner if (ioctl_data_tag[i] != NULL) 3656d743f04SSascha Wildner bus_dma_tag_destroy(ioctl_data_tag[i]); 3666d743f04SSascha Wildner } 3676d743f04SSascha Wildner 3686d743f04SSascha Wildner /* Free command */ 3696d743f04SSascha Wildner mrsas_release_mfi_cmd(cmd); 3706d743f04SSascha Wildner 3716d743f04SSascha Wildner return(ret); 3726d743f04SSascha Wildner } 3736d743f04SSascha Wildner 3746d743f04SSascha Wildner /** 3756d743f04SSascha Wildner * mrsas_alloc_mfi_cmds: Allocates the command packets 3766d743f04SSascha Wildner * input: Adapter instance soft state 3776d743f04SSascha Wildner * 3786d743f04SSascha Wildner * Each IOCTL or passthru command that is issued to the FW are wrapped in a 3796d743f04SSascha Wildner * local data structure called mrsas_mfi_cmd. The frame embedded in this 3806d743f04SSascha Wildner * mrsas_mfi is issued to FW. The array is used only to look up the 3816d743f04SSascha Wildner * mrsas_mfi_cmd given the context. The free commands are maintained in a 3826d743f04SSascha Wildner * linked list. 3836d743f04SSascha Wildner */ 3846d743f04SSascha Wildner int mrsas_alloc_mfi_cmds(struct mrsas_softc *sc) 3856d743f04SSascha Wildner { 3866d743f04SSascha Wildner int i, j; 3876d743f04SSascha Wildner u_int32_t max_cmd; 3886d743f04SSascha Wildner struct mrsas_mfi_cmd *cmd; 3896d743f04SSascha Wildner 3906d743f04SSascha Wildner max_cmd = MRSAS_MAX_MFI_CMDS; 3916d743f04SSascha Wildner 3926d743f04SSascha Wildner /* 3936d743f04SSascha Wildner * sc->mfi_cmd_list is an array of struct mrsas_mfi_cmd pointers. Allocate the 3946d743f04SSascha Wildner * dynamic array first and then allocate individual commands. 3956d743f04SSascha Wildner */ 3966d743f04SSascha Wildner sc->mfi_cmd_list = kmalloc(sizeof(struct mrsas_mfi_cmd*)*max_cmd, M_MRSAS, M_NOWAIT); 3976d743f04SSascha Wildner if (!sc->mfi_cmd_list) { 3986d743f04SSascha Wildner device_printf(sc->mrsas_dev, "Cannot alloc memory for mfi_cmd cmd_list.\n"); 3996d743f04SSascha Wildner return(ENOMEM); 4006d743f04SSascha Wildner } 4016d743f04SSascha Wildner memset(sc->mfi_cmd_list, 0, sizeof(struct mrsas_mfi_cmd *)*max_cmd); 4026d743f04SSascha Wildner for (i = 0; i < max_cmd; i++) { 4036d743f04SSascha Wildner sc->mfi_cmd_list[i] = kmalloc(sizeof(struct mrsas_mfi_cmd), 4046d743f04SSascha Wildner M_MRSAS, M_NOWAIT); 4056d743f04SSascha Wildner if (!sc->mfi_cmd_list[i]) { 4066d743f04SSascha Wildner for (j = 0; j < i; j++) 4076d743f04SSascha Wildner kfree(sc->mfi_cmd_list[j],M_MRSAS); 4086d743f04SSascha Wildner kfree(sc->mfi_cmd_list, M_MRSAS); 4096d743f04SSascha Wildner sc->mfi_cmd_list = NULL; 4106d743f04SSascha Wildner return(ENOMEM); 4116d743f04SSascha Wildner } 4126d743f04SSascha Wildner } 4136d743f04SSascha Wildner 4146d743f04SSascha Wildner for (i = 0; i < max_cmd; i++) { 4156d743f04SSascha Wildner cmd = sc->mfi_cmd_list[i]; 4166d743f04SSascha Wildner memset(cmd, 0, sizeof(struct mrsas_mfi_cmd)); 4176d743f04SSascha Wildner cmd->index = i; 4186d743f04SSascha Wildner cmd->ccb_ptr = NULL; 4196d743f04SSascha Wildner cmd->sc = sc; 4206d743f04SSascha Wildner TAILQ_INSERT_TAIL(&(sc->mrsas_mfi_cmd_list_head), cmd, next); 4216d743f04SSascha Wildner } 4226d743f04SSascha Wildner 4236d743f04SSascha Wildner /* create a frame pool and assign one frame to each command */ 4246d743f04SSascha Wildner if (mrsas_create_frame_pool(sc)) { 4256d743f04SSascha Wildner device_printf(sc->mrsas_dev, "Cannot allocate DMA frame pool.\n"); 4266d743f04SSascha Wildner for (i = 0; i < MRSAS_MAX_MFI_CMDS; i++) { // Free the frames 4276d743f04SSascha Wildner cmd = sc->mfi_cmd_list[i]; 4286d743f04SSascha Wildner mrsas_free_frame(sc, cmd); 4296d743f04SSascha Wildner } 4306d743f04SSascha Wildner if (sc->mficmd_frame_tag != NULL) 4316d743f04SSascha Wildner bus_dma_tag_destroy(sc->mficmd_frame_tag); 4326d743f04SSascha Wildner return(ENOMEM); 4336d743f04SSascha Wildner } 4346d743f04SSascha Wildner 4356d743f04SSascha Wildner return(0); 4366d743f04SSascha Wildner } 4376d743f04SSascha Wildner 4386d743f04SSascha Wildner /** 4396d743f04SSascha Wildner * mrsas_create_frame_pool - Creates DMA pool for cmd frames 4406d743f04SSascha Wildner * input: Adapter soft state 4416d743f04SSascha Wildner * 4426d743f04SSascha Wildner * Each command packet has an embedded DMA memory buffer that is used for 4436d743f04SSascha Wildner * filling MFI frame and the SG list that immediately follows the frame. This 4446d743f04SSascha Wildner * function creates those DMA memory buffers for each command packet by using 4456d743f04SSascha Wildner * PCI pool facility. pad_0 is initialized to 0 to prevent corrupting value 4466d743f04SSascha Wildner * of context and could cause FW crash. 4476d743f04SSascha Wildner */ 4486d743f04SSascha Wildner static int mrsas_create_frame_pool(struct mrsas_softc *sc) 4496d743f04SSascha Wildner { 4506d743f04SSascha Wildner int i; 4516d743f04SSascha Wildner struct mrsas_mfi_cmd *cmd; 4526d743f04SSascha Wildner 4536d743f04SSascha Wildner if (bus_dma_tag_create( sc->mrsas_parent_tag, // parent 4546d743f04SSascha Wildner 1, 0, // algnmnt, boundary 4556d743f04SSascha Wildner BUS_SPACE_MAXADDR_32BIT,// lowaddr 4566d743f04SSascha Wildner BUS_SPACE_MAXADDR, // highaddr 4576d743f04SSascha Wildner NULL, NULL, // filter, filterarg 4586d743f04SSascha Wildner MRSAS_MFI_FRAME_SIZE, // maxsize 4596d743f04SSascha Wildner 1, // msegments 4606d743f04SSascha Wildner MRSAS_MFI_FRAME_SIZE, // maxsegsize 4616d743f04SSascha Wildner BUS_DMA_ALLOCNOW, // flags 4626d743f04SSascha Wildner &sc->mficmd_frame_tag)) { 4636d743f04SSascha Wildner device_printf(sc->mrsas_dev, "Cannot create MFI frame tag\n"); 4646d743f04SSascha Wildner return (ENOMEM); 4656d743f04SSascha Wildner } 4666d743f04SSascha Wildner 4676d743f04SSascha Wildner for (i = 0; i < MRSAS_MAX_MFI_CMDS; i++) { 4686d743f04SSascha Wildner cmd = sc->mfi_cmd_list[i]; 4696d743f04SSascha Wildner cmd->frame = mrsas_alloc_frame(sc, cmd); 4706d743f04SSascha Wildner if (cmd->frame == NULL) { 4716d743f04SSascha Wildner device_printf(sc->mrsas_dev, "Cannot alloc MFI frame memory\n"); 4726d743f04SSascha Wildner return (ENOMEM); 4736d743f04SSascha Wildner } 4746d743f04SSascha Wildner memset(cmd->frame, 0, MRSAS_MFI_FRAME_SIZE); 4756d743f04SSascha Wildner cmd->frame->io.context = cmd->index; 4766d743f04SSascha Wildner cmd->frame->io.pad_0 = 0; 4776d743f04SSascha Wildner } 4786d743f04SSascha Wildner 4796d743f04SSascha Wildner return(0); 4806d743f04SSascha Wildner } 4816d743f04SSascha Wildner 4826d743f04SSascha Wildner /** 4836d743f04SSascha Wildner * mrsas_alloc_frame - Allocates MFI Frames 4846d743f04SSascha Wildner * input: Adapter soft state 4856d743f04SSascha Wildner * 4866d743f04SSascha Wildner * Create bus DMA memory tag and dmamap and load memory for MFI frames. 4876d743f04SSascha Wildner * Returns virtual memory pointer to allocated region. 4886d743f04SSascha Wildner */ 4896d743f04SSascha Wildner void *mrsas_alloc_frame(struct mrsas_softc *sc, struct mrsas_mfi_cmd *cmd) 4906d743f04SSascha Wildner { 4916d743f04SSascha Wildner u_int32_t frame_size = MRSAS_MFI_FRAME_SIZE; 4926d743f04SSascha Wildner 4936d743f04SSascha Wildner if (bus_dmamem_alloc(sc->mficmd_frame_tag, (void **)&cmd->frame_mem, 4946d743f04SSascha Wildner BUS_DMA_NOWAIT, &cmd->frame_dmamap)) { 4956d743f04SSascha Wildner device_printf(sc->mrsas_dev, "Cannot alloc MFI frame memory\n"); 4966d743f04SSascha Wildner return (NULL); 4976d743f04SSascha Wildner } 4986d743f04SSascha Wildner if (bus_dmamap_load(sc->mficmd_frame_tag, cmd->frame_dmamap, 4996d743f04SSascha Wildner cmd->frame_mem, frame_size, mrsas_alloc_cb, 5006d743f04SSascha Wildner &cmd->frame_phys_addr, BUS_DMA_NOWAIT)) { 5016d743f04SSascha Wildner device_printf(sc->mrsas_dev, "Cannot load IO request memory\n"); 5026d743f04SSascha Wildner return (NULL); 5036d743f04SSascha Wildner } 5046d743f04SSascha Wildner 5056d743f04SSascha Wildner return(cmd->frame_mem); 5066d743f04SSascha Wildner } 5076d743f04SSascha Wildner 5086d743f04SSascha Wildner /* 5096d743f04SSascha Wildner * mrsas_alloc_cb: Callback function of bus_dmamap_load() 5106d743f04SSascha Wildner * input: callback argument, 5116d743f04SSascha Wildner * machine dependent type that describes DMA segments, 5126d743f04SSascha Wildner * number of segments, 5136d743f04SSascha Wildner * error code. 5146d743f04SSascha Wildner * 5156d743f04SSascha Wildner * This function is for the driver to receive mapping information resultant 5166d743f04SSascha Wildner * of the bus_dmamap_load(). The information is actually not being used, 5176d743f04SSascha Wildner * but the address is saved anyway. 5186d743f04SSascha Wildner */ 5196d743f04SSascha Wildner static void mrsas_alloc_cb(void *arg, bus_dma_segment_t *segs, 5206d743f04SSascha Wildner int nsegs, int error) 5216d743f04SSascha Wildner { 5226d743f04SSascha Wildner bus_addr_t *addr; 5236d743f04SSascha Wildner 5246d743f04SSascha Wildner addr = arg; 5256d743f04SSascha Wildner *addr = segs[0].ds_addr; 5266d743f04SSascha Wildner } 5276d743f04SSascha Wildner 5286d743f04SSascha Wildner /** 5296d743f04SSascha Wildner * mrsas_free_frames: Frees memory for MFI frames 5306d743f04SSascha Wildner * input: Adapter soft state 5316d743f04SSascha Wildner * 5326d743f04SSascha Wildner * Deallocates MFI frames memory. Called from mrsas_free_mem() during 5336d743f04SSascha Wildner * detach and error case during creation of frame pool. 5346d743f04SSascha Wildner */ 5356d743f04SSascha Wildner void mrsas_free_frame(struct mrsas_softc *sc, struct mrsas_mfi_cmd *cmd) 5366d743f04SSascha Wildner { 5376d743f04SSascha Wildner if (cmd->frame_phys_addr) 5386d743f04SSascha Wildner bus_dmamap_unload(sc->mficmd_frame_tag, cmd->frame_dmamap); 5396d743f04SSascha Wildner if (cmd->frame_mem != NULL) 5406d743f04SSascha Wildner bus_dmamem_free(sc->mficmd_frame_tag, cmd->frame_mem, cmd->frame_dmamap); 5416d743f04SSascha Wildner } 542