/* * mr_sas.c: source for mr_sas driver * * Solaris MegaRAID device driver for SAS2.0 controllers * Copyright (c) 2008-2012, LSI Logic Corporation. * All rights reserved. * * Version: * Author: * Swaminathan K S * Arun Chandrashekhar * Manju R * Rasheed * Shakeel Bukhari * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the author nor the names of its contributors may be * used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011 Bayard G. Bell. All rights reserved. * Copyright 2013 Nexenta Systems, Inc. All rights reserved. * Copyright 2015, 2017 Citrus IT Limited. All rights reserved. * Copyright 2015 Garrett D'Amore * Copyright 2023 Oxide Computer Company */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* devfs_clean */ #include "mr_sas.h" /* * FMA header files */ #include #include #include #include /* Macros to help Skinny and stock 2108/MFI live together. */ #define WR_IB_PICK_QPORT(addr, instance) \ if ((instance)->skinny) { \ WR_IB_LOW_QPORT((addr), (instance)); \ WR_IB_HIGH_QPORT(0, (instance)); \ } else { \ WR_IB_QPORT((addr), (instance)); \ } /* * Local static data */ static void *mrsas_state = NULL; static volatile boolean_t mrsas_relaxed_ordering = B_TRUE; volatile int debug_level_g = CL_NONE; static volatile int msi_enable = 1; static volatile int ctio_enable = 1; /* Default Timeout value to issue online controller reset */ volatile int debug_timeout_g = 0xF0; /* 0xB4; */ /* Simulate consecutive firmware fault */ static volatile int debug_fw_faults_after_ocr_g = 0; #ifdef OCRDEBUG /* Simulate three consecutive timeout for an IO */ static volatile int debug_consecutive_timeout_after_ocr_g = 0; #endif #pragma weak scsi_hba_open #pragma weak scsi_hba_close #pragma weak scsi_hba_ioctl /* Local static prototypes. */ static int mrsas_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); static int mrsas_attach(dev_info_t *, ddi_attach_cmd_t); #ifdef __sparc static int mrsas_reset(dev_info_t *, ddi_reset_cmd_t); #else static int mrsas_quiesce(dev_info_t *); #endif static int mrsas_detach(dev_info_t *, ddi_detach_cmd_t); static int mrsas_open(dev_t *, int, int, cred_t *); static int mrsas_close(dev_t, int, int, cred_t *); static int mrsas_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); static int mrsas_tran_tgt_init(dev_info_t *, dev_info_t *, scsi_hba_tran_t *, struct scsi_device *); static struct scsi_pkt *mrsas_tran_init_pkt(struct scsi_address *, register struct scsi_pkt *, struct buf *, int, int, int, int, int (*)(), caddr_t); static int mrsas_tran_start(struct scsi_address *, register struct scsi_pkt *); static int mrsas_tran_abort(struct scsi_address *, struct scsi_pkt *); static int mrsas_tran_reset(struct scsi_address *, int); static int mrsas_tran_getcap(struct scsi_address *, char *, int); static int mrsas_tran_setcap(struct scsi_address *, char *, int, int); static void mrsas_tran_destroy_pkt(struct scsi_address *, struct scsi_pkt *); static void mrsas_tran_dmafree(struct scsi_address *, struct scsi_pkt *); static void mrsas_tran_sync_pkt(struct scsi_address *, struct scsi_pkt *); static int mrsas_tran_quiesce(dev_info_t *dip); static int mrsas_tran_unquiesce(dev_info_t *dip); static uint_t mrsas_isr(caddr_t, caddr_t); static uint_t mrsas_softintr(); static void mrsas_undo_resources(dev_info_t *, struct mrsas_instance *); static void free_space_for_mfi(struct mrsas_instance *); static uint32_t read_fw_status_reg_ppc(struct mrsas_instance *); static void issue_cmd_ppc(struct mrsas_cmd *, struct mrsas_instance *); static int issue_cmd_in_poll_mode_ppc(struct mrsas_instance *, struct mrsas_cmd *); static int issue_cmd_in_sync_mode_ppc(struct mrsas_instance *, struct mrsas_cmd *); static void enable_intr_ppc(struct mrsas_instance *); static void disable_intr_ppc(struct mrsas_instance *); static int intr_ack_ppc(struct mrsas_instance *); static void flush_cache(struct mrsas_instance *instance); void display_scsi_inquiry(caddr_t); static int start_mfi_aen(struct mrsas_instance *instance); static int handle_drv_ioctl(struct mrsas_instance *instance, struct mrsas_ioctl *ioctl, int mode); static int handle_mfi_ioctl(struct mrsas_instance *instance, struct mrsas_ioctl *ioctl, int mode); static int handle_mfi_aen(struct mrsas_instance *instance, struct mrsas_aen *aen); static struct mrsas_cmd *build_cmd(struct mrsas_instance *, struct scsi_address *, struct scsi_pkt *, uchar_t *); static int alloc_additional_dma_buffer(struct mrsas_instance *); static void complete_cmd_in_sync_mode(struct mrsas_instance *, struct mrsas_cmd *); static int mrsas_kill_adapter(struct mrsas_instance *); static int mrsas_issue_init_mfi(struct mrsas_instance *); static int mrsas_reset_ppc(struct mrsas_instance *); static uint32_t mrsas_initiate_ocr_if_fw_is_faulty(struct mrsas_instance *); static int wait_for_outstanding(struct mrsas_instance *instance); static int register_mfi_aen(struct mrsas_instance *instance, uint32_t seq_num, uint32_t class_locale_word); static int issue_mfi_pthru(struct mrsas_instance *instance, struct mrsas_ioctl *ioctl, struct mrsas_cmd *cmd, int mode); static int issue_mfi_dcmd(struct mrsas_instance *instance, struct mrsas_ioctl *ioctl, struct mrsas_cmd *cmd, int mode); static int issue_mfi_smp(struct mrsas_instance *instance, struct mrsas_ioctl *ioctl, struct mrsas_cmd *cmd, int mode); static int issue_mfi_stp(struct mrsas_instance *instance, struct mrsas_ioctl *ioctl, struct mrsas_cmd *cmd, int mode); static int abort_aen_cmd(struct mrsas_instance *instance, struct mrsas_cmd *cmd_to_abort); static void mrsas_rem_intrs(struct mrsas_instance *instance); static int mrsas_add_intrs(struct mrsas_instance *instance, int intr_type); static void mrsas_tran_tgt_free(dev_info_t *, dev_info_t *, scsi_hba_tran_t *, struct scsi_device *); static int mrsas_tran_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t, void *, dev_info_t **); static int mrsas_parse_devname(char *, int *, int *); static int mrsas_config_all_devices(struct mrsas_instance *); static int mrsas_config_ld(struct mrsas_instance *, uint16_t, uint8_t, dev_info_t **); static int mrsas_name_node(dev_info_t *, char *, int); static void mrsas_issue_evt_taskq(struct mrsas_eventinfo *); static void free_additional_dma_buffer(struct mrsas_instance *); static void io_timeout_checker(void *); static void mrsas_fm_init(struct mrsas_instance *); static void mrsas_fm_fini(struct mrsas_instance *); static struct mrsas_function_template mrsas_function_template_ppc = { .read_fw_status_reg = read_fw_status_reg_ppc, .issue_cmd = issue_cmd_ppc, .issue_cmd_in_sync_mode = issue_cmd_in_sync_mode_ppc, .issue_cmd_in_poll_mode = issue_cmd_in_poll_mode_ppc, .enable_intr = enable_intr_ppc, .disable_intr = disable_intr_ppc, .intr_ack = intr_ack_ppc, .init_adapter = mrsas_init_adapter_ppc }; static struct mrsas_function_template mrsas_function_template_fusion = { .read_fw_status_reg = tbolt_read_fw_status_reg, .issue_cmd = tbolt_issue_cmd, .issue_cmd_in_sync_mode = tbolt_issue_cmd_in_sync_mode, .issue_cmd_in_poll_mode = tbolt_issue_cmd_in_poll_mode, .enable_intr = tbolt_enable_intr, .disable_intr = tbolt_disable_intr, .intr_ack = tbolt_intr_ack, .init_adapter = mrsas_init_adapter_tbolt }; ddi_dma_attr_t mrsas_generic_dma_attr = { DMA_ATTR_V0, /* dma_attr_version */ 0, /* low DMA address range */ 0xFFFFFFFFU, /* high DMA address range */ 0xFFFFFFFFU, /* DMA counter register */ 8, /* DMA address alignment */ 0x07, /* DMA burstsizes */ 1, /* min DMA size */ 0xFFFFFFFFU, /* max DMA size */ 0xFFFFFFFFU, /* segment boundary */ MRSAS_MAX_SGE_CNT, /* dma_attr_sglen */ 512, /* granularity of device */ 0 /* bus specific DMA flags */ }; int32_t mrsas_max_cap_maxxfer = 0x1000000; /* * Fix for: Thunderbolt controller IO timeout when IO write size is 1MEG, * Limit size to 256K */ uint32_t mrsas_tbolt_max_cap_maxxfer = (512 * 512); /* * cb_ops contains base level routines */ static struct cb_ops mrsas_cb_ops = { mrsas_open, /* open */ mrsas_close, /* close */ nodev, /* strategy */ nodev, /* print */ nodev, /* dump */ nodev, /* read */ nodev, /* write */ mrsas_ioctl, /* ioctl */ nodev, /* devmap */ nodev, /* mmap */ nodev, /* segmap */ nochpoll, /* poll */ nodev, /* cb_prop_op */ 0, /* streamtab */ D_NEW | D_HOTPLUG, /* cb_flag */ CB_REV, /* cb_rev */ nodev, /* cb_aread */ nodev /* cb_awrite */ }; /* * dev_ops contains configuration routines */ static struct dev_ops mrsas_ops = { DEVO_REV, /* rev, */ 0, /* refcnt */ mrsas_getinfo, /* getinfo */ nulldev, /* identify */ nulldev, /* probe */ mrsas_attach, /* attach */ mrsas_detach, /* detach */ #ifdef __sparc mrsas_reset, /* reset */ #else /* __sparc */ nodev, #endif /* __sparc */ &mrsas_cb_ops, /* char/block ops */ NULL, /* bus ops */ NULL, /* power */ #ifdef __sparc ddi_quiesce_not_needed #else /* __sparc */ mrsas_quiesce /* quiesce */ #endif /* __sparc */ }; static struct modldrv modldrv = { &mod_driverops, /* module type - driver */ MRSAS_VERSION, &mrsas_ops, /* driver ops */ }; static struct modlinkage modlinkage = { MODREV_1, /* ml_rev - must be MODREV_1 */ &modldrv, /* ml_linkage */ NULL /* end of driver linkage */ }; static struct ddi_device_acc_attr endian_attr = { DDI_DEVICE_ATTR_V1, DDI_STRUCTURE_LE_ACC, DDI_STRICTORDER_ACC, DDI_DEFAULT_ACC }; /* Use the LSI Fast Path for the 2208 (tbolt) commands. */ unsigned int enable_fp = 1; /* * ************************************************************************** * * * * common entry points - for loadable kernel modules * * * * ************************************************************************** * */ /* * _init - initialize a loadable module * @void * * The driver should perform any one-time resource allocation or data * initialization during driver loading in _init(). For example, the driver * should initialize any mutexes global to the driver in this routine. * The driver should not, however, use _init() to allocate or initialize * anything that has to do with a particular instance of the device. * Per-instance initialization must be done in attach(). */ int _init(void) { int ret; con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); ret = ddi_soft_state_init(&mrsas_state, sizeof (struct mrsas_instance), 0); if (ret != DDI_SUCCESS) { cmn_err(CE_WARN, "mr_sas: could not init state"); return (ret); } if ((ret = scsi_hba_init(&modlinkage)) != DDI_SUCCESS) { cmn_err(CE_WARN, "mr_sas: could not init scsi hba"); ddi_soft_state_fini(&mrsas_state); return (ret); } ret = mod_install(&modlinkage); if (ret != DDI_SUCCESS) { cmn_err(CE_WARN, "mr_sas: mod_install failed"); scsi_hba_fini(&modlinkage); ddi_soft_state_fini(&mrsas_state); } return (ret); } /* * _info - returns information about a loadable module. * @void * * _info() is called to return module information. This is a typical entry * point that does predefined role. It simply calls mod_info(). */ int _info(struct modinfo *modinfop) { con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); return (mod_info(&modlinkage, modinfop)); } /* * _fini - prepare a loadable module for unloading * @void * * In _fini(), the driver should release any resources that were allocated in * _init(). The driver must remove itself from the system module list. */ int _fini(void) { int ret; con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); if ((ret = mod_remove(&modlinkage)) != DDI_SUCCESS) { con_log(CL_ANN1, (CE_WARN, "_fini: mod_remove() failed, error 0x%X", ret)); return (ret); } scsi_hba_fini(&modlinkage); con_log(CL_DLEVEL1, (CE_NOTE, "_fini: scsi_hba_fini() done.")); ddi_soft_state_fini(&mrsas_state); con_log(CL_DLEVEL1, (CE_NOTE, "_fini: ddi_soft_state_fini() done.")); return (ret); } /* * ************************************************************************** * * * * common entry points - for autoconfiguration * * * * ************************************************************************** * */ /* * attach - adds a device to the system as part of initialization * @dip: * @cmd: * * The kernel calls a driver's attach() entry point to attach an instance of * a device (for MegaRAID, it is instance of a controller) or to resume * operation for an instance of a device that has been suspended or has been * shut down by the power management framework * The attach() entry point typically includes the following types of * processing: * - allocate a soft-state structure for the device instance (for MegaRAID, * controller instance) * - initialize per-instance mutexes * - initialize condition variables * - register the device's interrupts (for MegaRAID, controller's interrupts) * - map the registers and memory of the device instance (for MegaRAID, * controller instance) * - create minor device nodes for the device instance (for MegaRAID, * controller instance) * - report that the device instance (for MegaRAID, controller instance) has * attached */ static int mrsas_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { int instance_no; int nregs; int i = 0; uint8_t irq; uint16_t vendor_id; uint16_t device_id; uint16_t subsysvid; uint16_t subsysid; uint16_t command; off_t reglength = 0; int intr_types = 0; char *data; scsi_hba_tran_t *tran; ddi_dma_attr_t tran_dma_attr; struct mrsas_instance *instance; con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); /* CONSTCOND */ ASSERT(NO_COMPETING_THREADS); instance_no = ddi_get_instance(dip); /* * check to see whether this device is in a DMA-capable slot. */ if (ddi_slaveonly(dip) == DDI_SUCCESS) { dev_err(dip, CE_WARN, "Device in slave-only slot, unused"); return (DDI_FAILURE); } switch (cmd) { case DDI_ATTACH: /* allocate the soft state for the instance */ if (ddi_soft_state_zalloc(mrsas_state, instance_no) != DDI_SUCCESS) { dev_err(dip, CE_WARN, "Failed to allocate soft state"); return (DDI_FAILURE); } instance = (struct mrsas_instance *)ddi_get_soft_state (mrsas_state, instance_no); if (instance == NULL) { dev_err(dip, CE_WARN, "Bad soft state"); ddi_soft_state_free(mrsas_state, instance_no); return (DDI_FAILURE); } instance->unroll.softs = 1; /* Setup the PCI configuration space handles */ if (pci_config_setup(dip, &instance->pci_handle) != DDI_SUCCESS) { dev_err(dip, CE_WARN, "pci config setup failed"); ddi_soft_state_free(mrsas_state, instance_no); return (DDI_FAILURE); } if (ddi_dev_nregs(dip, &nregs) != DDI_SUCCESS) { dev_err(dip, CE_WARN, "Failed to get registers"); pci_config_teardown(&instance->pci_handle); ddi_soft_state_free(mrsas_state, instance_no); return (DDI_FAILURE); } vendor_id = pci_config_get16(instance->pci_handle, PCI_CONF_VENID); device_id = pci_config_get16(instance->pci_handle, PCI_CONF_DEVID); subsysvid = pci_config_get16(instance->pci_handle, PCI_CONF_SUBVENID); subsysid = pci_config_get16(instance->pci_handle, PCI_CONF_SUBSYSID); pci_config_put16(instance->pci_handle, PCI_CONF_COMM, (pci_config_get16(instance->pci_handle, PCI_CONF_COMM) | PCI_COMM_ME)); irq = pci_config_get8(instance->pci_handle, PCI_CONF_ILINE); dev_err(dip, CE_CONT, "?0x%x:0x%x 0x%x:0x%x, irq:%d drv-ver:%s\n", vendor_id, device_id, subsysvid, subsysid, irq, MRSAS_VERSION); /* enable bus-mastering */ command = pci_config_get16(instance->pci_handle, PCI_CONF_COMM); if (!(command & PCI_COMM_ME)) { command |= PCI_COMM_ME; pci_config_put16(instance->pci_handle, PCI_CONF_COMM, command); con_log(CL_ANN, (CE_CONT, "mr_sas%d: " "enable bus-mastering", instance_no)); } else { con_log(CL_DLEVEL1, (CE_CONT, "mr_sas%d: " "bus-mastering already set", instance_no)); } /* initialize function pointers */ switch (device_id) { case PCI_DEVICE_ID_LSI_INVADER: case PCI_DEVICE_ID_LSI_FURY: case PCI_DEVICE_ID_LSI_INTRUDER: case PCI_DEVICE_ID_LSI_INTRUDER_24: case PCI_DEVICE_ID_LSI_CUTLASS_52: case PCI_DEVICE_ID_LSI_CUTLASS_53: dev_err(dip, CE_CONT, "?Gen3 device detected\n"); instance->gen3 = 1; /* FALLTHROUGH */ case PCI_DEVICE_ID_LSI_TBOLT: dev_err(dip, CE_CONT, "?TBOLT device detected\n"); instance->func_ptr = &mrsas_function_template_fusion; instance->tbolt = 1; break; case PCI_DEVICE_ID_LSI_SKINNY: case PCI_DEVICE_ID_LSI_SKINNY_NEW: /* * FALLTHRU to PPC-style functions, but mark this * instance as Skinny, because the register set is * slightly different (See WR_IB_PICK_QPORT), and * certain other features are available to a Skinny * HBA. */ dev_err(dip, CE_CONT, "?Skinny device detected\n"); instance->skinny = 1; /* FALLTHRU */ case PCI_DEVICE_ID_LSI_2108VDE: case PCI_DEVICE_ID_LSI_2108V: dev_err(dip, CE_CONT, "?2108 Liberator device detected\n"); instance->func_ptr = &mrsas_function_template_ppc; break; default: dev_err(dip, CE_WARN, "Invalid device detected"); pci_config_teardown(&instance->pci_handle); ddi_soft_state_free(mrsas_state, instance_no); return (DDI_FAILURE); } instance->baseaddress = pci_config_get32( instance->pci_handle, PCI_CONF_BASE0); instance->baseaddress &= 0x0fffc; instance->dip = dip; instance->vendor_id = vendor_id; instance->device_id = device_id; instance->subsysvid = subsysvid; instance->subsysid = subsysid; instance->instance = instance_no; /* Initialize FMA */ instance->fm_capabilities = ddi_prop_get_int( DDI_DEV_T_ANY, instance->dip, DDI_PROP_DONTPASS, "fm-capable", DDI_FM_EREPORT_CAPABLE | DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE | DDI_FM_ERRCB_CAPABLE); mrsas_fm_init(instance); /* Setup register map */ if ((ddi_dev_regsize(instance->dip, REGISTER_SET_IO_2108, ®length) != DDI_SUCCESS) || reglength < MINIMUM_MFI_MEM_SZ) { goto fail_attach; } if (reglength > DEFAULT_MFI_MEM_SZ) { reglength = DEFAULT_MFI_MEM_SZ; con_log(CL_DLEVEL1, (CE_NOTE, "mr_sas: register length to map is 0x%lx bytes", reglength)); } if (ddi_regs_map_setup(instance->dip, REGISTER_SET_IO_2108, &instance->regmap, 0, reglength, &endian_attr, &instance->regmap_handle) != DDI_SUCCESS) { dev_err(dip, CE_WARN, "couldn't map control registers"); goto fail_attach; } instance->unroll.regs = 1; /* * Disable Interrupt Now. * Setup Software interrupt */ instance->func_ptr->disable_intr(instance); if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 0, "mrsas-enable-msi", &data) == DDI_SUCCESS) { if (strncmp(data, "no", 3) == 0) { msi_enable = 0; con_log(CL_ANN1, (CE_WARN, "msi_enable = %d disabled", msi_enable)); } ddi_prop_free(data); } dev_err(dip, CE_CONT, "?msi_enable = %d\n", msi_enable); if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 0, "mrsas-enable-fp", &data) == DDI_SUCCESS) { if (strncmp(data, "no", 3) == 0) { enable_fp = 0; dev_err(dip, CE_NOTE, "enable_fp = %d, Fast-Path disabled.\n", enable_fp); } ddi_prop_free(data); } dev_err(dip, CE_CONT, "?enable_fp = %d\n", enable_fp); /* Check for all supported interrupt types */ if (ddi_intr_get_supported_types( dip, &intr_types) != DDI_SUCCESS) { dev_err(dip, CE_WARN, "ddi_intr_get_supported_types() failed"); goto fail_attach; } con_log(CL_DLEVEL1, (CE_NOTE, "ddi_intr_get_supported_types() ret: 0x%x", intr_types)); /* Initialize and Setup Interrupt handler */ if (msi_enable && (intr_types & DDI_INTR_TYPE_MSIX)) { if (mrsas_add_intrs(instance, DDI_INTR_TYPE_MSIX) != DDI_SUCCESS) { dev_err(dip, CE_WARN, "MSIX interrupt query failed"); goto fail_attach; } instance->intr_type = DDI_INTR_TYPE_MSIX; } else if (msi_enable && (intr_types & DDI_INTR_TYPE_MSI)) { if (mrsas_add_intrs(instance, DDI_INTR_TYPE_MSI) != DDI_SUCCESS) { dev_err(dip, CE_WARN, "MSI interrupt query failed"); goto fail_attach; } instance->intr_type = DDI_INTR_TYPE_MSI; } else if (intr_types & DDI_INTR_TYPE_FIXED) { msi_enable = 0; if (mrsas_add_intrs(instance, DDI_INTR_TYPE_FIXED) != DDI_SUCCESS) { dev_err(dip, CE_WARN, "FIXED interrupt query failed"); goto fail_attach; } instance->intr_type = DDI_INTR_TYPE_FIXED; } else { dev_err(dip, CE_WARN, "Device cannot " "suppport either FIXED or MSI/X " "interrupts"); goto fail_attach; } instance->unroll.intr = 1; if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 0, "mrsas-enable-ctio", &data) == DDI_SUCCESS) { if (strncmp(data, "no", 3) == 0) { ctio_enable = 0; con_log(CL_ANN1, (CE_WARN, "ctio_enable = %d disabled", ctio_enable)); } ddi_prop_free(data); } dev_err(dip, CE_CONT, "?ctio_enable = %d\n", ctio_enable); /* setup the mfi based low level driver */ if (mrsas_init_adapter(instance) != DDI_SUCCESS) { dev_err(dip, CE_WARN, "could not initialize the low level driver"); goto fail_attach; } /* Initialize all Mutex */ INIT_LIST_HEAD(&instance->completed_pool_list); mutex_init(&instance->completed_pool_mtx, NULL, MUTEX_DRIVER, DDI_INTR_PRI(instance->intr_pri)); mutex_init(&instance->sync_map_mtx, NULL, MUTEX_DRIVER, DDI_INTR_PRI(instance->intr_pri)); mutex_init(&instance->app_cmd_pool_mtx, NULL, MUTEX_DRIVER, DDI_INTR_PRI(instance->intr_pri)); mutex_init(&instance->config_dev_mtx, NULL, MUTEX_DRIVER, DDI_INTR_PRI(instance->intr_pri)); mutex_init(&instance->cmd_pend_mtx, NULL, MUTEX_DRIVER, DDI_INTR_PRI(instance->intr_pri)); mutex_init(&instance->ocr_flags_mtx, NULL, MUTEX_DRIVER, DDI_INTR_PRI(instance->intr_pri)); mutex_init(&instance->int_cmd_mtx, NULL, MUTEX_DRIVER, DDI_INTR_PRI(instance->intr_pri)); cv_init(&instance->int_cmd_cv, NULL, CV_DRIVER, NULL); mutex_init(&instance->cmd_pool_mtx, NULL, MUTEX_DRIVER, DDI_INTR_PRI(instance->intr_pri)); mutex_init(&instance->reg_write_mtx, NULL, MUTEX_DRIVER, DDI_INTR_PRI(instance->intr_pri)); if (instance->tbolt) { mutex_init(&instance->cmd_app_pool_mtx, NULL, MUTEX_DRIVER, DDI_INTR_PRI(instance->intr_pri)); mutex_init(&instance->chip_mtx, NULL, MUTEX_DRIVER, DDI_INTR_PRI(instance->intr_pri)); } instance->unroll.mutexs = 1; instance->timeout_id = (timeout_id_t)-1; /* Register our soft-isr for highlevel interrupts. */ instance->isr_level = instance->intr_pri; if (!(instance->tbolt)) { if (instance->isr_level == HIGH_LEVEL_INTR) { if (ddi_add_softintr(dip, DDI_SOFTINT_HIGH, &instance->soft_intr_id, NULL, NULL, mrsas_softintr, (caddr_t)instance) != DDI_SUCCESS) { dev_err(dip, CE_WARN, "Software ISR did not register"); goto fail_attach; } instance->unroll.soft_isr = 1; } } instance->softint_running = 0; /* Allocate a transport structure */ tran = scsi_hba_tran_alloc(dip, SCSI_HBA_CANSLEEP); if (tran == NULL) { dev_err(dip, CE_WARN, "scsi_hba_tran_alloc failed"); goto fail_attach; } instance->tran = tran; instance->unroll.tran = 1; tran->tran_hba_private = instance; tran->tran_tgt_init = mrsas_tran_tgt_init; tran->tran_tgt_probe = scsi_hba_probe; tran->tran_tgt_free = mrsas_tran_tgt_free; tran->tran_init_pkt = mrsas_tran_init_pkt; if (instance->tbolt) tran->tran_start = mrsas_tbolt_tran_start; else tran->tran_start = mrsas_tran_start; tran->tran_abort = mrsas_tran_abort; tran->tran_reset = mrsas_tran_reset; tran->tran_getcap = mrsas_tran_getcap; tran->tran_setcap = mrsas_tran_setcap; tran->tran_destroy_pkt = mrsas_tran_destroy_pkt; tran->tran_dmafree = mrsas_tran_dmafree; tran->tran_sync_pkt = mrsas_tran_sync_pkt; tran->tran_quiesce = mrsas_tran_quiesce; tran->tran_unquiesce = mrsas_tran_unquiesce; tran->tran_bus_config = mrsas_tran_bus_config; if (mrsas_relaxed_ordering) mrsas_generic_dma_attr.dma_attr_flags |= DDI_DMA_RELAXED_ORDERING; tran_dma_attr = mrsas_generic_dma_attr; tran_dma_attr.dma_attr_sgllen = instance->max_num_sge; /* Attach this instance of the hba */ if (scsi_hba_attach_setup(dip, &tran_dma_attr, tran, 0) != DDI_SUCCESS) { dev_err(dip, CE_WARN, "scsi_hba_attach failed"); goto fail_attach; } instance->unroll.tranSetup = 1; con_log(CL_ANN1, (CE_CONT, "scsi_hba_attach_setup() done.")); /* create devctl node for cfgadm command */ if (ddi_create_minor_node(dip, "devctl", S_IFCHR, INST2DEVCTL(instance_no), DDI_NT_SCSI_NEXUS, 0) == DDI_FAILURE) { dev_err(dip, CE_WARN, "failed to create devctl node."); goto fail_attach; } instance->unroll.devctl = 1; /* create scsi node for cfgadm command */ if (ddi_create_minor_node(dip, "scsi", S_IFCHR, INST2SCSI(instance_no), DDI_NT_SCSI_ATTACHMENT_POINT, 0) == DDI_FAILURE) { dev_err(dip, CE_WARN, "failed to create scsi node."); goto fail_attach; } instance->unroll.scsictl = 1; (void) snprintf(instance->iocnode, sizeof (instance->iocnode), "%d:lsirdctl", instance_no); /* * Create a node for applications * for issuing ioctl to the driver. */ if (ddi_create_minor_node(dip, instance->iocnode, S_IFCHR, INST2LSIRDCTL(instance_no), DDI_PSEUDO, 0) == DDI_FAILURE) { dev_err(dip, CE_WARN, "failed to create ioctl node."); goto fail_attach; } instance->unroll.ioctl = 1; /* Create a taskq to handle dr events */ if ((instance->taskq = ddi_taskq_create(dip, "mrsas_dr_taskq", 1, TASKQ_DEFAULTPRI, 0)) == NULL) { dev_err(dip, CE_WARN, "failed to create taskq."); instance->taskq = NULL; goto fail_attach; } instance->unroll.taskq = 1; con_log(CL_ANN1, (CE_CONT, "ddi_taskq_create() done.")); /* enable interrupt */ instance->func_ptr->enable_intr(instance); /* initiate AEN */ if (start_mfi_aen(instance)) { dev_err(dip, CE_WARN, "failed to initiate AEN."); goto fail_attach; } instance->unroll.aenPend = 1; con_log(CL_ANN1, (CE_CONT, "AEN started for instance %d.", instance_no)); /* Finally! We are on the air. */ ddi_report_dev(dip); /* FMA handle checking. */ if (mrsas_check_acc_handle(instance->regmap_handle) != DDI_SUCCESS) { goto fail_attach; } if (mrsas_check_acc_handle(instance->pci_handle) != DDI_SUCCESS) { goto fail_attach; } instance->mr_ld_list = kmem_zalloc(MRDRV_MAX_LD * sizeof (struct mrsas_ld), KM_SLEEP); instance->unroll.ldlist_buff = 1; if (instance->tbolt || instance->skinny) { instance->mr_tbolt_pd_max = MRSAS_TBOLT_PD_TGT_MAX; instance->mr_tbolt_pd_list = kmem_zalloc(MRSAS_TBOLT_GET_PD_MAX(instance) * sizeof (struct mrsas_tbolt_pd), KM_SLEEP); ASSERT(instance->mr_tbolt_pd_list); for (i = 0; i < instance->mr_tbolt_pd_max; i++) { instance->mr_tbolt_pd_list[i].lun_type = MRSAS_TBOLT_PD_LUN; instance->mr_tbolt_pd_list[i].dev_id = (uint8_t)i; } instance->unroll.pdlist_buff = 1; } break; case DDI_PM_RESUME: con_log(CL_ANN, (CE_NOTE, "mr_sas: DDI_PM_RESUME")); break; case DDI_RESUME: con_log(CL_ANN, (CE_NOTE, "mr_sas: DDI_RESUME")); break; default: con_log(CL_ANN, (CE_WARN, "mr_sas: invalid attach cmd=%x", cmd)); return (DDI_FAILURE); } con_log(CL_DLEVEL1, (CE_NOTE, "mrsas_attach() return SUCCESS instance_num %d", instance_no)); return (DDI_SUCCESS); fail_attach: mrsas_undo_resources(dip, instance); mrsas_fm_ereport(instance, DDI_FM_DEVICE_NO_RESPONSE); ddi_fm_service_impact(instance->dip, DDI_SERVICE_LOST); mrsas_fm_fini(instance); pci_config_teardown(&instance->pci_handle); ddi_soft_state_free(mrsas_state, instance_no); return (DDI_FAILURE); } /* * getinfo - gets device information * @dip: * @cmd: * @arg: * @resultp: * * The system calls getinfo() to obtain configuration information that only * the driver knows. The mapping of minor numbers to device instance is * entirely under the control of the driver. The system sometimes needs to ask * the driver which device a particular dev_t represents. * Given the device number return the devinfo pointer from the scsi_device * structure. */ /*ARGSUSED*/ static int mrsas_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) { int rval; int mrsas_minor = getminor((dev_t)arg); struct mrsas_instance *instance; con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); switch (cmd) { case DDI_INFO_DEVT2DEVINFO: instance = (struct mrsas_instance *) ddi_get_soft_state(mrsas_state, MINOR2INST(mrsas_minor)); if (instance == NULL) { *resultp = NULL; rval = DDI_FAILURE; } else { *resultp = instance->dip; rval = DDI_SUCCESS; } break; case DDI_INFO_DEVT2INSTANCE: *resultp = (void *)(intptr_t) (MINOR2INST(getminor((dev_t)arg))); rval = DDI_SUCCESS; break; default: *resultp = NULL; rval = DDI_FAILURE; } return (rval); } /* * detach - detaches a device from the system * @dip: pointer to the device's dev_info structure * @cmd: type of detach * * A driver's detach() entry point is called to detach an instance of a device * that is bound to the driver. The entry point is called with the instance of * the device node to be detached and with DDI_DETACH, which is specified as * the cmd argument to the entry point. * This routine is called during driver unload. We free all the allocated * resources and call the corresponding LLD so that it can also release all * its resources. */ static int mrsas_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { int instance_no; struct mrsas_instance *instance; con_log(CL_ANN, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); /* CONSTCOND */ ASSERT(NO_COMPETING_THREADS); instance_no = ddi_get_instance(dip); instance = (struct mrsas_instance *)ddi_get_soft_state(mrsas_state, instance_no); if (!instance) { dev_err(dip, CE_WARN, "could not get instance in detach"); return (DDI_FAILURE); } switch (cmd) { case DDI_DETACH: con_log(CL_ANN, (CE_NOTE, "mrsas_detach: DDI_DETACH")); mutex_enter(&instance->config_dev_mtx); if (instance->timeout_id != (timeout_id_t)-1) { mutex_exit(&instance->config_dev_mtx); (void) untimeout(instance->timeout_id); instance->timeout_id = (timeout_id_t)-1; mutex_enter(&instance->config_dev_mtx); instance->unroll.timer = 0; } mutex_exit(&instance->config_dev_mtx); if (instance->unroll.tranSetup == 1) { if (scsi_hba_detach(dip) != DDI_SUCCESS) { dev_err(dip, CE_WARN, "failed to detach"); return (DDI_FAILURE); } instance->unroll.tranSetup = 0; con_log(CL_ANN1, (CE_CONT, "scsi_hba_dettach() done.")); } flush_cache(instance); mrsas_undo_resources(dip, instance); mrsas_fm_fini(instance); pci_config_teardown(&instance->pci_handle); ddi_soft_state_free(mrsas_state, instance_no); break; case DDI_PM_SUSPEND: con_log(CL_ANN, (CE_NOTE, "mrsas_detach: DDI_PM_SUSPEND")); break; case DDI_SUSPEND: con_log(CL_ANN, (CE_NOTE, "mrsas_detach: DDI_SUSPEND")); break; default: con_log(CL_ANN, (CE_WARN, "invalid detach command:0x%x", cmd)); return (DDI_FAILURE); } return (DDI_SUCCESS); } static void mrsas_undo_resources(dev_info_t *dip, struct mrsas_instance *instance) { con_log(CL_ANN, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); if (instance->unroll.ioctl == 1) { ddi_remove_minor_node(dip, instance->iocnode); instance->unroll.ioctl = 0; } if (instance->unroll.scsictl == 1) { ddi_remove_minor_node(dip, "scsi"); instance->unroll.scsictl = 0; } if (instance->unroll.devctl == 1) { ddi_remove_minor_node(dip, "devctl"); instance->unroll.devctl = 0; } if (instance->unroll.tranSetup == 1) { if (scsi_hba_detach(dip) != DDI_SUCCESS) { dev_err(dip, CE_WARN, "failed to detach"); return; /* DDI_FAILURE */ } instance->unroll.tranSetup = 0; con_log(CL_ANN1, (CE_CONT, "scsi_hba_dettach() done.")); } if (instance->unroll.tran == 1) { scsi_hba_tran_free(instance->tran); instance->unroll.tran = 0; con_log(CL_ANN1, (CE_CONT, "scsi_hba_tran_free() done.")); } if (instance->unroll.syncCmd == 1) { if (instance->tbolt) { if (abort_syncmap_cmd(instance, instance->map_update_cmd)) { dev_err(dip, CE_WARN, "mrsas_detach: " "failed to abort previous syncmap command"); } instance->unroll.syncCmd = 0; con_log(CL_ANN1, (CE_CONT, "sync cmd aborted, done.")); } } if (instance->unroll.aenPend == 1) { if (abort_aen_cmd(instance, instance->aen_cmd)) dev_err(dip, CE_WARN, "mrsas_detach: " "failed to abort prevous AEN command"); instance->unroll.aenPend = 0; con_log(CL_ANN1, (CE_CONT, "aen cmd aborted, done.")); /* This means the controller is fully initialized and running */ /* Shutdown should be a last command to controller. */ /* shutdown_controller(); */ } if (instance->unroll.timer == 1) { if (instance->timeout_id != (timeout_id_t)-1) { (void) untimeout(instance->timeout_id); instance->timeout_id = (timeout_id_t)-1; instance->unroll.timer = 0; } } instance->func_ptr->disable_intr(instance); if (instance->unroll.mutexs == 1) { mutex_destroy(&instance->cmd_pool_mtx); mutex_destroy(&instance->app_cmd_pool_mtx); mutex_destroy(&instance->cmd_pend_mtx); mutex_destroy(&instance->completed_pool_mtx); mutex_destroy(&instance->sync_map_mtx); mutex_destroy(&instance->int_cmd_mtx); cv_destroy(&instance->int_cmd_cv); mutex_destroy(&instance->config_dev_mtx); mutex_destroy(&instance->ocr_flags_mtx); mutex_destroy(&instance->reg_write_mtx); if (instance->tbolt) { mutex_destroy(&instance->cmd_app_pool_mtx); mutex_destroy(&instance->chip_mtx); } instance->unroll.mutexs = 0; con_log(CL_ANN1, (CE_CONT, "Destroy mutex & cv, done.")); } if (instance->unroll.soft_isr == 1) { ddi_remove_softintr(instance->soft_intr_id); instance->unroll.soft_isr = 0; } if (instance->unroll.intr == 1) { mrsas_rem_intrs(instance); instance->unroll.intr = 0; } if (instance->unroll.taskq == 1) { if (instance->taskq) { ddi_taskq_destroy(instance->taskq); instance->unroll.taskq = 0; } } /* * free dma memory allocated for * cmds/frames/queues/driver version etc */ if (instance->unroll.verBuff == 1) { (void) mrsas_free_dma_obj(instance, instance->drv_ver_dma_obj); instance->unroll.verBuff = 0; } if (instance->unroll.pdlist_buff == 1) { if (instance->mr_tbolt_pd_list != NULL) { kmem_free(instance->mr_tbolt_pd_list, MRSAS_TBOLT_GET_PD_MAX(instance) * sizeof (struct mrsas_tbolt_pd)); } instance->mr_tbolt_pd_list = NULL; instance->unroll.pdlist_buff = 0; } if (instance->unroll.ldlist_buff == 1) { if (instance->mr_ld_list != NULL) { kmem_free(instance->mr_ld_list, MRDRV_MAX_LD * sizeof (struct mrsas_ld)); } instance->mr_ld_list = NULL; instance->unroll.ldlist_buff = 0; } if (instance->tbolt) { if (instance->unroll.alloc_space_mpi2 == 1) { free_space_for_mpi2(instance); instance->unroll.alloc_space_mpi2 = 0; } } else { if (instance->unroll.alloc_space_mfi == 1) { free_space_for_mfi(instance); instance->unroll.alloc_space_mfi = 0; } } if (instance->unroll.regs == 1) { ddi_regs_map_free(&instance->regmap_handle); instance->unroll.regs = 0; con_log(CL_ANN1, (CE_CONT, "ddi_regs_map_free() done.")); } } /* * ************************************************************************** * * * * common entry points - for character driver types * * * * ************************************************************************** * */ /* * open - gets access to a device * @dev: * @openflags: * @otyp: * @credp: * * Access to a device by one or more application programs is controlled * through the open() and close() entry points. The primary function of * open() is to verify that the open request is allowed. */ static int mrsas_open(dev_t *dev, int openflags, int otyp, cred_t *credp) { int rval = 0; con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); /* Check root permissions */ if (drv_priv(credp) != 0) { con_log(CL_ANN, (CE_WARN, "mr_sas: Non-root ioctl access denied!")); return (EPERM); } /* Verify we are being opened as a character device */ if (otyp != OTYP_CHR) { con_log(CL_ANN, (CE_WARN, "mr_sas: ioctl node must be a char node")); return (EINVAL); } if (ddi_get_soft_state(mrsas_state, MINOR2INST(getminor(*dev))) == NULL) { return (ENXIO); } if (scsi_hba_open) { rval = scsi_hba_open(dev, openflags, otyp, credp); } return (rval); } /* * close - gives up access to a device * @dev: * @openflags: * @otyp: * @credp: * * close() should perform any cleanup necessary to finish using the minor * device, and prepare the device (and driver) to be opened again. */ static int mrsas_close(dev_t dev, int openflags, int otyp, cred_t *credp) { int rval = 0; con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); /* no need for locks! */ if (scsi_hba_close) { rval = scsi_hba_close(dev, openflags, otyp, credp); } return (rval); } /* * ioctl - performs a range of I/O commands for character drivers * @dev: * @cmd: * @arg: * @mode: * @credp: * @rvalp: * * ioctl() routine must make sure that user data is copied into or out of the * kernel address space explicitly using copyin(), copyout(), ddi_copyin(), * and ddi_copyout(), as appropriate. * This is a wrapper routine to serialize access to the actual ioctl routine. * ioctl() should return 0 on success, or the appropriate error number. The * driver may also set the value returned to the calling process through rvalp. */ static int mrsas_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) { int rval = 0; struct mrsas_instance *instance; struct mrsas_ioctl *ioctl; struct mrsas_aen aen; con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); instance = ddi_get_soft_state(mrsas_state, MINOR2INST(getminor(dev))); if (instance == NULL) { /* invalid minor number */ con_log(CL_ANN, (CE_WARN, "mr_sas: adapter not found.")); return (ENXIO); } ioctl = (struct mrsas_ioctl *)kmem_zalloc(sizeof (struct mrsas_ioctl), KM_SLEEP); ASSERT(ioctl); switch ((uint_t)cmd) { case MRSAS_IOCTL_FIRMWARE: if (ddi_copyin((void *)arg, ioctl, sizeof (struct mrsas_ioctl), mode)) { con_log(CL_ANN, (CE_WARN, "mrsas_ioctl: " "ERROR IOCTL copyin")); kmem_free(ioctl, sizeof (struct mrsas_ioctl)); return (EFAULT); } if (ioctl->control_code == MRSAS_DRIVER_IOCTL_COMMON) { rval = handle_drv_ioctl(instance, ioctl, mode); } else { rval = handle_mfi_ioctl(instance, ioctl, mode); } if (ddi_copyout((void *)ioctl, (void *)arg, (sizeof (struct mrsas_ioctl) - 1), mode)) { con_log(CL_ANN, (CE_WARN, "mrsas_ioctl: copy_to_user failed")); rval = 1; } break; case MRSAS_IOCTL_AEN: if (ddi_copyin((void *) arg, &aen, sizeof (struct mrsas_aen), mode)) { con_log(CL_ANN, (CE_WARN, "mrsas_ioctl: ERROR AEN copyin")); kmem_free(ioctl, sizeof (struct mrsas_ioctl)); return (EFAULT); } rval = handle_mfi_aen(instance, &aen); if (ddi_copyout((void *) &aen, (void *)arg, sizeof (struct mrsas_aen), mode)) { con_log(CL_ANN, (CE_WARN, "mrsas_ioctl: copy_to_user failed")); rval = 1; } break; default: rval = scsi_hba_ioctl(dev, cmd, arg, mode, credp, rvalp); con_log(CL_DLEVEL1, (CE_NOTE, "mrsas_ioctl: " "scsi_hba_ioctl called, ret = %x.", rval)); } kmem_free(ioctl, sizeof (struct mrsas_ioctl)); return (rval); } /* * ************************************************************************** * * * * common entry points - for block driver types * * * * ************************************************************************** * */ #ifdef __sparc /* * reset - TBD * @dip: * @cmd: * * TBD */ /*ARGSUSED*/ static int mrsas_reset(dev_info_t *dip, ddi_reset_cmd_t cmd) { int instance_no; struct mrsas_instance *instance; instance_no = ddi_get_instance(dip); instance = (struct mrsas_instance *)ddi_get_soft_state (mrsas_state, instance_no); con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); if (!instance) { con_log(CL_ANN, (CE_WARN, "mr_sas:%d could not get adapter " "in reset", instance_no)); return (DDI_FAILURE); } instance->func_ptr->disable_intr(instance); con_log(CL_ANN1, (CE_CONT, "flushing cache for instance %d", instance_no)); flush_cache(instance); return (DDI_SUCCESS); } #else /* __sparc */ /*ARGSUSED*/ static int mrsas_quiesce(dev_info_t *dip) { int instance_no; struct mrsas_instance *instance; instance_no = ddi_get_instance(dip); instance = (struct mrsas_instance *)ddi_get_soft_state (mrsas_state, instance_no); con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); if (!instance) { con_log(CL_ANN1, (CE_WARN, "mr_sas:%d could not get adapter " "in quiesce", instance_no)); return (DDI_FAILURE); } if (instance->deadadapter || instance->adapterresetinprogress) { con_log(CL_ANN1, (CE_WARN, "mr_sas:%d adapter is not in " "healthy state", instance_no)); return (DDI_FAILURE); } if (abort_aen_cmd(instance, instance->aen_cmd)) { con_log(CL_ANN1, (CE_WARN, "mrsas_quiesce: " "failed to abort prevous AEN command QUIESCE")); } if (instance->tbolt) { if (abort_syncmap_cmd(instance, instance->map_update_cmd)) { dev_err(dip, CE_WARN, "mrsas_detach: failed to abort " "previous syncmap command"); return (DDI_FAILURE); } } instance->func_ptr->disable_intr(instance); con_log(CL_ANN1, (CE_CONT, "flushing cache for instance %d", instance_no)); flush_cache(instance); if (wait_for_outstanding(instance)) { con_log(CL_ANN1, (CE_CONT, "wait_for_outstanding: return FAIL.\n")); return (DDI_FAILURE); } return (DDI_SUCCESS); } #endif /* __sparc */ /* * ************************************************************************** * * * * entry points (SCSI HBA) * * * * ************************************************************************** * */ /* * tran_tgt_init - initialize a target device instance * @hba_dip: * @tgt_dip: * @tran: * @sd: * * The tran_tgt_init() entry point enables the HBA to allocate and initialize * any per-target resources. tran_tgt_init() also enables the HBA to qualify * the device's address as valid and supportable for that particular HBA. * By returning DDI_FAILURE, the instance of the target driver for that device * is not probed or attached. */ /*ARGSUSED*/ static int mrsas_tran_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip, scsi_hba_tran_t *tran, struct scsi_device *sd) { struct mrsas_instance *instance; uint16_t tgt = sd->sd_address.a_target; uint8_t lun = sd->sd_address.a_lun; dev_info_t *child = NULL; con_log(CL_DLEVEL2, (CE_NOTE, "mrsas_tgt_init target %d lun %d", tgt, lun)); instance = ADDR2MR(&sd->sd_address); if (ndi_dev_is_persistent_node(tgt_dip) == 0) { /* * If no persistent node exists, we don't allow .conf node * to be created. */ if ((child = mrsas_find_child(instance, tgt, lun)) != NULL) { con_log(CL_DLEVEL2, (CE_NOTE, "mrsas_tgt_init find child =" " %p t = %d l = %d", (void *)child, tgt, lun)); if (ndi_merge_node(tgt_dip, mrsas_name_node) != DDI_SUCCESS) /* Create this .conf node */ return (DDI_SUCCESS); } con_log(CL_DLEVEL2, (CE_NOTE, "mrsas_tgt_init in ndi_per " "DDI_FAILURE t = %d l = %d", tgt, lun)); return (DDI_FAILURE); } con_log(CL_DLEVEL2, (CE_NOTE, "mrsas_tgt_init dev_dip %p tgt_dip %p", (void *)instance->mr_ld_list[tgt].dip, (void *)tgt_dip)); if (tgt < MRDRV_MAX_LD && lun == 0) { if (instance->mr_ld_list[tgt].dip == NULL && strcmp(ddi_driver_name(sd->sd_dev), "sd") == 0) { mutex_enter(&instance->config_dev_mtx); instance->mr_ld_list[tgt].dip = tgt_dip; instance->mr_ld_list[tgt].lun_type = MRSAS_LD_LUN; instance->mr_ld_list[tgt].flag = MRDRV_TGT_VALID; mutex_exit(&instance->config_dev_mtx); } } else if (instance->tbolt || instance->skinny) { if (instance->mr_tbolt_pd_list[tgt].dip == NULL) { mutex_enter(&instance->config_dev_mtx); instance->mr_tbolt_pd_list[tgt].dip = tgt_dip; instance->mr_tbolt_pd_list[tgt].flag = MRDRV_TGT_VALID; mutex_exit(&instance->config_dev_mtx); con_log(CL_ANN1, (CE_NOTE, "mrsas_tran_tgt_init:" "t%xl%x", tgt, lun)); } } return (DDI_SUCCESS); } /*ARGSUSED*/ static void mrsas_tran_tgt_free(dev_info_t *hba_dip, dev_info_t *tgt_dip, scsi_hba_tran_t *hba_tran, struct scsi_device *sd) { struct mrsas_instance *instance; int tgt = sd->sd_address.a_target; int lun = sd->sd_address.a_lun; instance = ADDR2MR(&sd->sd_address); con_log(CL_DLEVEL2, (CE_NOTE, "tgt_free t = %d l = %d", tgt, lun)); if (tgt < MRDRV_MAX_LD && lun == 0) { if (instance->mr_ld_list[tgt].dip == tgt_dip) { mutex_enter(&instance->config_dev_mtx); instance->mr_ld_list[tgt].dip = NULL; mutex_exit(&instance->config_dev_mtx); } } else if (instance->tbolt || instance->skinny) { mutex_enter(&instance->config_dev_mtx); instance->mr_tbolt_pd_list[tgt].dip = NULL; mutex_exit(&instance->config_dev_mtx); con_log(CL_ANN1, (CE_NOTE, "tgt_free: Setting dip = NULL" "for tgt:%x", tgt)); } } dev_info_t * mrsas_find_child(struct mrsas_instance *instance, uint16_t tgt, uint8_t lun) { dev_info_t *child = NULL; char addr[SCSI_MAXNAMELEN]; char tmp[MAXNAMELEN]; (void) snprintf(addr, sizeof (addr), "%x,%x", tgt, lun); for (child = ddi_get_child(instance->dip); child; child = ddi_get_next_sibling(child)) { if (ndi_dev_is_persistent_node(child) == 0) { continue; } if (mrsas_name_node(child, tmp, MAXNAMELEN) != DDI_SUCCESS) { continue; } if (strcmp(addr, tmp) == 0) { break; } } con_log(CL_DLEVEL2, (CE_NOTE, "mrsas_find_child: return child = %p", (void *)child)); return (child); } /* * mrsas_name_node - * @dip: * @name: * @len: */ static int mrsas_name_node(dev_info_t *dip, char *name, int len) { int tgt, lun; tgt = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "target", -1); con_log(CL_DLEVEL2, (CE_NOTE, "mrsas_name_node: dip %p tgt %d", (void *)dip, tgt)); if (tgt == -1) { return (DDI_FAILURE); } lun = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "lun", -1); con_log(CL_DLEVEL2, (CE_NOTE, "mrsas_name_node: tgt %d lun %d", tgt, lun)); if (lun == -1) { return (DDI_FAILURE); } (void) snprintf(name, len, "%x,%x", tgt, lun); return (DDI_SUCCESS); } /* * tran_init_pkt - allocate & initialize a scsi_pkt structure * @ap: * @pkt: * @bp: * @cmdlen: * @statuslen: * @tgtlen: * @flags: * @callback: * * The tran_init_pkt() entry point allocates and initializes a scsi_pkt * structure and DMA resources for a target driver request. The * tran_init_pkt() entry point is called when the target driver calls the * SCSA function scsi_init_pkt(). Each call of the tran_init_pkt() entry point * is a request to perform one or more of three possible services: * - allocation and initialization of a scsi_pkt structure * - allocation of DMA resources for data transfer * - reallocation of DMA resources for the next portion of the data transfer */ static struct scsi_pkt * mrsas_tran_init_pkt(struct scsi_address *ap, register struct scsi_pkt *pkt, struct buf *bp, int cmdlen, int statuslen, int tgtlen, int flags, int (*callback)(), caddr_t arg) { struct scsa_cmd *acmd; struct mrsas_instance *instance; struct scsi_pkt *new_pkt; con_log(CL_DLEVEL1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); instance = ADDR2MR(ap); /* step #1 : pkt allocation */ if (pkt == NULL) { pkt = scsi_hba_pkt_alloc(instance->dip, ap, cmdlen, statuslen, tgtlen, sizeof (struct scsa_cmd), callback, arg); if (pkt == NULL) { return (NULL); } acmd = PKT2CMD(pkt); /* * Initialize the new pkt - we redundantly initialize * all the fields for illustrative purposes. */ acmd->cmd_pkt = pkt; acmd->cmd_flags = 0; acmd->cmd_scblen = statuslen; acmd->cmd_cdblen = cmdlen; acmd->cmd_dmahandle = NULL; acmd->cmd_ncookies = 0; acmd->cmd_cookie = 0; acmd->cmd_cookiecnt = 0; acmd->cmd_nwin = 0; pkt->pkt_address = *ap; pkt->pkt_comp = (void (*)())NULL; pkt->pkt_flags = 0; pkt->pkt_time = 0; pkt->pkt_resid = 0; pkt->pkt_state = 0; pkt->pkt_statistics = 0; pkt->pkt_reason = 0; new_pkt = pkt; } else { acmd = PKT2CMD(pkt); new_pkt = NULL; } /* step #2 : dma allocation/move */ if (bp && bp->b_bcount != 0) { if (acmd->cmd_dmahandle == NULL) { if (mrsas_dma_alloc(instance, pkt, bp, flags, callback) == DDI_FAILURE) { if (new_pkt) { scsi_hba_pkt_free(ap, new_pkt); } return ((struct scsi_pkt *)NULL); } } else { if (mrsas_dma_move(instance, pkt, bp) == DDI_FAILURE) { return ((struct scsi_pkt *)NULL); } } } return (pkt); } /* * tran_start - transport a SCSI command to the addressed target * @ap: * @pkt: * * The tran_start() entry point for a SCSI HBA driver is called to transport a * SCSI command to the addressed target. The SCSI command is described * entirely within the scsi_pkt structure, which the target driver allocated * through the HBA driver's tran_init_pkt() entry point. If the command * involves a data transfer, DMA resources must also have been allocated for * the scsi_pkt structure. * * Return Values : * TRAN_BUSY - request queue is full, no more free scbs * TRAN_ACCEPT - pkt has been submitted to the instance */ static int mrsas_tran_start(struct scsi_address *ap, register struct scsi_pkt *pkt) { uchar_t cmd_done = 0; struct mrsas_instance *instance = ADDR2MR(ap); struct mrsas_cmd *cmd; con_log(CL_DLEVEL1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); if (instance->deadadapter == 1) { con_log(CL_ANN1, (CE_WARN, "mrsas_tran_start: return TRAN_FATAL_ERROR " "for IO, as the HBA doesnt take any more IOs")); if (pkt) { pkt->pkt_reason = CMD_DEV_GONE; pkt->pkt_statistics = STAT_DISCON; } return (TRAN_FATAL_ERROR); } if (instance->adapterresetinprogress) { con_log(CL_ANN1, (CE_NOTE, "mrsas_tran_start: Reset flag set, " "returning mfi_pkt and setting TRAN_BUSY\n")); return (TRAN_BUSY); } con_log(CL_ANN1, (CE_CONT, "chkpnt:%s:%d:SCSI CDB[0]=0x%x time:%x", __func__, __LINE__, pkt->pkt_cdbp[0], pkt->pkt_time)); pkt->pkt_reason = CMD_CMPLT; *pkt->pkt_scbp = STATUS_GOOD; /* clear arq scsi_status */ cmd = build_cmd(instance, ap, pkt, &cmd_done); /* * Check if the command is already completed by the mrsas_build_cmd() * routine. In which case the busy_flag would be clear and scb will be * NULL and appropriate reason provided in pkt_reason field */ if (cmd_done) { pkt->pkt_reason = CMD_CMPLT; pkt->pkt_scbp[0] = STATUS_GOOD; pkt->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD; if (((pkt->pkt_flags & FLAG_NOINTR) == 0) && pkt->pkt_comp) { (*pkt->pkt_comp)(pkt); } return (TRAN_ACCEPT); } if (cmd == NULL) { return (TRAN_BUSY); } if ((pkt->pkt_flags & FLAG_NOINTR) == 0) { if (instance->fw_outstanding > instance->max_fw_cmds) { con_log(CL_ANN, (CE_CONT, "mr_sas:Firmware busy")); DTRACE_PROBE2(start_tran_err, uint16_t, instance->fw_outstanding, uint16_t, instance->max_fw_cmds); mrsas_return_mfi_pkt(instance, cmd); return (TRAN_BUSY); } /* Synchronize the Cmd frame for the controller */ (void) ddi_dma_sync(cmd->frame_dma_obj.dma_handle, 0, 0, DDI_DMA_SYNC_FORDEV); con_log(CL_ANN, (CE_CONT, "issue_cmd_ppc: SCSI CDB[0]=0x%x" "cmd->index:%x\n", pkt->pkt_cdbp[0], cmd->index)); instance->func_ptr->issue_cmd(cmd, instance); } else { struct mrsas_header *hdr = &cmd->frame->hdr; instance->func_ptr->issue_cmd_in_poll_mode(instance, cmd); pkt->pkt_reason = CMD_CMPLT; pkt->pkt_statistics = 0; pkt->pkt_state |= STATE_XFERRED_DATA | STATE_GOT_STATUS; switch (ddi_get8(cmd->frame_dma_obj.acc_handle, &hdr->cmd_status)) { case MFI_STAT_OK: pkt->pkt_scbp[0] = STATUS_GOOD; break; case MFI_STAT_SCSI_DONE_WITH_ERROR: con_log(CL_ANN, (CE_CONT, "mrsas_tran_start: scsi done with error")); pkt->pkt_reason = CMD_CMPLT; pkt->pkt_statistics = 0; ((struct scsi_status *)pkt->pkt_scbp)->sts_chk = 1; break; case MFI_STAT_DEVICE_NOT_FOUND: con_log(CL_ANN, (CE_CONT, "mrsas_tran_start: device not found error")); pkt->pkt_reason = CMD_DEV_GONE; pkt->pkt_statistics = STAT_DISCON; break; default: ((struct scsi_status *)pkt->pkt_scbp)->sts_busy = 1; } (void) mrsas_common_check(instance, cmd); DTRACE_PROBE2(start_nointr_done, uint8_t, hdr->cmd, uint8_t, hdr->cmd_status); mrsas_return_mfi_pkt(instance, cmd); if (pkt->pkt_comp) { (*pkt->pkt_comp)(pkt); } } return (TRAN_ACCEPT); } /* * tran_abort - Abort any commands that are currently in transport * @ap: * @pkt: * * The tran_abort() entry point for a SCSI HBA driver is called to abort any * commands that are currently in transport for a particular target. This entry * point is called when a target driver calls scsi_abort(). The tran_abort() * entry point should attempt to abort the command denoted by the pkt * parameter. If the pkt parameter is NULL, tran_abort() should attempt to * abort all outstanding commands in the transport layer for the particular * target or logical unit. */ /*ARGSUSED*/ static int mrsas_tran_abort(struct scsi_address *ap, struct scsi_pkt *pkt) { con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); /* abort command not supported by H/W */ return (DDI_FAILURE); } /* * tran_reset - reset either the SCSI bus or target * @ap: * @level: * * The tran_reset() entry point for a SCSI HBA driver is called to reset either * the SCSI bus or a particular SCSI target device. This entry point is called * when a target driver calls scsi_reset(). The tran_reset() entry point must * reset the SCSI bus if level is RESET_ALL. If level is RESET_TARGET, just the * particular target or logical unit must be reset. */ /*ARGSUSED*/ static int mrsas_tran_reset(struct scsi_address *ap, int level) { struct mrsas_instance *instance = ADDR2MR(ap); con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); if (wait_for_outstanding(instance)) { con_log(CL_ANN1, (CE_CONT, "wait_for_outstanding: return FAIL.\n")); return (DDI_FAILURE); } else { return (DDI_SUCCESS); } } /* * tran_getcap - get one of a set of SCSA-defined capabilities * @ap: * @cap: * @whom: * * The target driver can request the current setting of the capability for a * particular target by setting the whom parameter to nonzero. A whom value of * zero indicates a request for the current setting of the general capability * for the SCSI bus or for adapter hardware. The tran_getcap() should return -1 * for undefined capabilities or the current value of the requested capability. */ /*ARGSUSED*/ static int mrsas_tran_getcap(struct scsi_address *ap, char *cap, int whom) { int rval = 0; struct mrsas_instance *instance = ADDR2MR(ap); con_log(CL_DLEVEL2, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); /* we do allow inquiring about capabilities for other targets */ if (cap == NULL) { return (-1); } switch (scsi_hba_lookup_capstr(cap)) { case SCSI_CAP_DMA_MAX: if (instance->tbolt) { /* Limit to 256k max transfer */ rval = mrsas_tbolt_max_cap_maxxfer; } else { /* Limit to 16MB max transfer */ rval = mrsas_max_cap_maxxfer; } break; case SCSI_CAP_MSG_OUT: rval = 1; break; case SCSI_CAP_DISCONNECT: rval = 0; break; case SCSI_CAP_SYNCHRONOUS: rval = 0; break; case SCSI_CAP_WIDE_XFER: rval = 1; break; case SCSI_CAP_TAGGED_QING: rval = 1; break; case SCSI_CAP_UNTAGGED_QING: rval = 1; break; case SCSI_CAP_PARITY: rval = 1; break; case SCSI_CAP_INITIATOR_ID: rval = instance->init_id; break; case SCSI_CAP_ARQ: rval = 1; break; case SCSI_CAP_LINKED_CMDS: rval = 0; break; case SCSI_CAP_RESET_NOTIFICATION: rval = 1; break; case SCSI_CAP_GEOMETRY: rval = -1; break; default: con_log(CL_DLEVEL2, (CE_NOTE, "Default cap coming 0x%x", scsi_hba_lookup_capstr(cap))); rval = -1; break; } return (rval); } /* * tran_setcap - set one of a set of SCSA-defined capabilities * @ap: * @cap: * @value: * @whom: * * The target driver might request that the new value be set for a particular * target by setting the whom parameter to nonzero. A whom value of zero * means that request is to set the new value for the SCSI bus or for adapter * hardware in general. * The tran_setcap() should return the following values as appropriate: * - -1 for undefined capabilities * - 0 if the HBA driver cannot set the capability to the requested value * - 1 if the HBA driver is able to set the capability to the requested value */ /*ARGSUSED*/ static int mrsas_tran_setcap(struct scsi_address *ap, char *cap, int value, int whom) { int rval = 1; con_log(CL_DLEVEL2, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); /* We don't allow setting capabilities for other targets */ if (cap == NULL || whom == 0) { return (-1); } switch (scsi_hba_lookup_capstr(cap)) { case SCSI_CAP_DMA_MAX: case SCSI_CAP_MSG_OUT: case SCSI_CAP_PARITY: case SCSI_CAP_LINKED_CMDS: case SCSI_CAP_RESET_NOTIFICATION: case SCSI_CAP_DISCONNECT: case SCSI_CAP_SYNCHRONOUS: case SCSI_CAP_UNTAGGED_QING: case SCSI_CAP_WIDE_XFER: case SCSI_CAP_INITIATOR_ID: case SCSI_CAP_ARQ: /* * None of these are settable via * the capability interface. */ break; case SCSI_CAP_TAGGED_QING: rval = 1; break; case SCSI_CAP_SECTOR_SIZE: rval = 1; break; case SCSI_CAP_TOTAL_SECTORS: rval = 1; break; default: rval = -1; break; } return (rval); } /* * tran_destroy_pkt - deallocate scsi_pkt structure * @ap: * @pkt: * * The tran_destroy_pkt() entry point is the HBA driver function that * deallocates scsi_pkt structures. The tran_destroy_pkt() entry point is * called when the target driver calls scsi_destroy_pkt(). The * tran_destroy_pkt() entry point must free any DMA resources that have been * allocated for the packet. An implicit DMA synchronization occurs if the * DMA resources are freed and any cached data remains after the completion * of the transfer. */ static void mrsas_tran_destroy_pkt(struct scsi_address *ap, struct scsi_pkt *pkt) { struct scsa_cmd *acmd = PKT2CMD(pkt); con_log(CL_DLEVEL2, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); if (acmd->cmd_flags & CFLAG_DMAVALID) { acmd->cmd_flags &= ~CFLAG_DMAVALID; (void) ddi_dma_unbind_handle(acmd->cmd_dmahandle); ddi_dma_free_handle(&acmd->cmd_dmahandle); acmd->cmd_dmahandle = NULL; } /* free the pkt */ scsi_hba_pkt_free(ap, pkt); } /* * tran_dmafree - deallocates DMA resources * @ap: * @pkt: * * The tran_dmafree() entry point deallocates DMAQ resources that have been * allocated for a scsi_pkt structure. The tran_dmafree() entry point is * called when the target driver calls scsi_dmafree(). The tran_dmafree() must * free only DMA resources allocated for a scsi_pkt structure, not the * scsi_pkt itself. When DMA resources are freed, a DMA synchronization is * implicitly performed. */ /*ARGSUSED*/ static void mrsas_tran_dmafree(struct scsi_address *ap, struct scsi_pkt *pkt) { register struct scsa_cmd *acmd = PKT2CMD(pkt); con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); if (acmd->cmd_flags & CFLAG_DMAVALID) { acmd->cmd_flags &= ~CFLAG_DMAVALID; (void) ddi_dma_unbind_handle(acmd->cmd_dmahandle); ddi_dma_free_handle(&acmd->cmd_dmahandle); acmd->cmd_dmahandle = NULL; } } /* * tran_sync_pkt - synchronize the DMA object allocated * @ap: * @pkt: * * The tran_sync_pkt() entry point synchronizes the DMA object allocated for * the scsi_pkt structure before or after a DMA transfer. The tran_sync_pkt() * entry point is called when the target driver calls scsi_sync_pkt(). If the * data transfer direction is a DMA read from device to memory, tran_sync_pkt() * must synchronize the CPU's view of the data. If the data transfer direction * is a DMA write from memory to device, tran_sync_pkt() must synchronize the * device's view of the data. */ /*ARGSUSED*/ static void mrsas_tran_sync_pkt(struct scsi_address *ap, struct scsi_pkt *pkt) { register struct scsa_cmd *acmd = PKT2CMD(pkt); con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); if (acmd->cmd_flags & CFLAG_DMAVALID) { (void) ddi_dma_sync(acmd->cmd_dmahandle, acmd->cmd_dma_offset, acmd->cmd_dma_len, (acmd->cmd_flags & CFLAG_DMASEND) ? DDI_DMA_SYNC_FORDEV : DDI_DMA_SYNC_FORCPU); } } /*ARGSUSED*/ static int mrsas_tran_quiesce(dev_info_t *dip) { con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); return (1); } /*ARGSUSED*/ static int mrsas_tran_unquiesce(dev_info_t *dip) { con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); return (1); } /* * mrsas_isr(caddr_t, caddr_t) * * The Interrupt Service Routine * * Collect status for all completed commands and do callback * */ static uint_t mrsas_isr(caddr_t arg1, caddr_t arg2 __unused) { struct mrsas_instance *instance = (struct mrsas_instance *)arg1; int need_softintr; uint32_t producer; uint32_t consumer; uint32_t context; int retval; struct mrsas_cmd *cmd; struct mrsas_header *hdr; struct scsi_pkt *pkt; con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); ASSERT(instance); if (instance->tbolt) { mutex_enter(&instance->chip_mtx); if ((instance->intr_type == DDI_INTR_TYPE_FIXED) && !(instance->func_ptr->intr_ack(instance))) { mutex_exit(&instance->chip_mtx); return (DDI_INTR_UNCLAIMED); } retval = mr_sas_tbolt_process_outstanding_cmd(instance); mutex_exit(&instance->chip_mtx); return (retval); } else { if ((instance->intr_type == DDI_INTR_TYPE_FIXED) && !instance->func_ptr->intr_ack(instance)) { return (DDI_INTR_UNCLAIMED); } } (void) ddi_dma_sync(instance->mfi_internal_dma_obj.dma_handle, 0, 0, DDI_DMA_SYNC_FORCPU); if (mrsas_check_dma_handle(instance->mfi_internal_dma_obj.dma_handle) != DDI_SUCCESS) { mrsas_fm_ereport(instance, DDI_FM_DEVICE_NO_RESPONSE); ddi_fm_service_impact(instance->dip, DDI_SERVICE_LOST); con_log(CL_ANN1, (CE_WARN, "mr_sas_isr(): FMA check, returning DDI_INTR_UNCLAIMED")); return (DDI_INTR_CLAIMED); } con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); #ifdef OCRDEBUG if (debug_consecutive_timeout_after_ocr_g == 1) { con_log(CL_ANN1, (CE_NOTE, "simulating consecutive timeout after ocr")); return (DDI_INTR_CLAIMED); } #endif mutex_enter(&instance->completed_pool_mtx); mutex_enter(&instance->cmd_pend_mtx); producer = ddi_get32(instance->mfi_internal_dma_obj.acc_handle, instance->producer); consumer = ddi_get32(instance->mfi_internal_dma_obj.acc_handle, instance->consumer); con_log(CL_ANN, (CE_CONT, " producer %x consumer %x ", producer, consumer)); if (producer == consumer) { con_log(CL_ANN, (CE_WARN, "producer == consumer case")); DTRACE_PROBE2(isr_pc_err, uint32_t, producer, uint32_t, consumer); mutex_exit(&instance->cmd_pend_mtx); mutex_exit(&instance->completed_pool_mtx); return (DDI_INTR_CLAIMED); } while (consumer != producer) { context = ddi_get32(instance->mfi_internal_dma_obj.acc_handle, &instance->reply_queue[consumer]); cmd = instance->cmd_list[context]; if (cmd->sync_cmd == MRSAS_TRUE) { hdr = (struct mrsas_header *)&cmd->frame->hdr; if (hdr) { mlist_del_init(&cmd->list); } } else { pkt = cmd->pkt; if (pkt) { mlist_del_init(&cmd->list); } } mlist_add_tail(&cmd->list, &instance->completed_pool_list); consumer++; if (consumer == (instance->max_fw_cmds + 1)) { consumer = 0; } } ddi_put32(instance->mfi_internal_dma_obj.acc_handle, instance->consumer, consumer); mutex_exit(&instance->cmd_pend_mtx); mutex_exit(&instance->completed_pool_mtx); (void) ddi_dma_sync(instance->mfi_internal_dma_obj.dma_handle, 0, 0, DDI_DMA_SYNC_FORDEV); if (instance->softint_running) { need_softintr = 0; } else { need_softintr = 1; } if (instance->isr_level == HIGH_LEVEL_INTR) { if (need_softintr) { ddi_trigger_softintr(instance->soft_intr_id); } } else { /* * Not a high-level interrupt, therefore call the soft level * interrupt explicitly */ (void) mrsas_softintr(instance); } return (DDI_INTR_CLAIMED); } /* * ************************************************************************** * * * * libraries * * * * ************************************************************************** * */ /* * get_mfi_pkt : Get a command from the free pool * After successful allocation, the caller of this routine * must clear the frame buffer (memset to zero) before * using the packet further. * * ***** Note ***** * After clearing the frame buffer the context id of the * frame buffer SHOULD be restored back. */ struct mrsas_cmd * mrsas_get_mfi_pkt(struct mrsas_instance *instance) { mlist_t *head = &instance->cmd_pool_list; struct mrsas_cmd *cmd = NULL; mutex_enter(&instance->cmd_pool_mtx); if (!mlist_empty(head)) { cmd = mlist_entry(head->next, struct mrsas_cmd, list); mlist_del_init(head->next); } if (cmd != NULL) { cmd->pkt = NULL; cmd->retry_count_for_ocr = 0; cmd->drv_pkt_time = 0; } mutex_exit(&instance->cmd_pool_mtx); return (cmd); } static struct mrsas_cmd * get_mfi_app_pkt(struct mrsas_instance *instance) { mlist_t *head = &instance->app_cmd_pool_list; struct mrsas_cmd *cmd = NULL; mutex_enter(&instance->app_cmd_pool_mtx); if (!mlist_empty(head)) { cmd = mlist_entry(head->next, struct mrsas_cmd, list); mlist_del_init(head->next); } if (cmd != NULL) { cmd->pkt = NULL; cmd->retry_count_for_ocr = 0; cmd->drv_pkt_time = 0; } mutex_exit(&instance->app_cmd_pool_mtx); return (cmd); } /* * return_mfi_pkt : Return a cmd to free command pool */ void mrsas_return_mfi_pkt(struct mrsas_instance *instance, struct mrsas_cmd *cmd) { mutex_enter(&instance->cmd_pool_mtx); /* use mlist_add_tail for debug assistance */ mlist_add_tail(&cmd->list, &instance->cmd_pool_list); mutex_exit(&instance->cmd_pool_mtx); } static void return_mfi_app_pkt(struct mrsas_instance *instance, struct mrsas_cmd *cmd) { mutex_enter(&instance->app_cmd_pool_mtx); mlist_add(&cmd->list, &instance->app_cmd_pool_list); mutex_exit(&instance->app_cmd_pool_mtx); } void push_pending_mfi_pkt(struct mrsas_instance *instance, struct mrsas_cmd *cmd) { struct scsi_pkt *pkt; struct mrsas_header *hdr; con_log(CL_DLEVEL2, (CE_NOTE, "push_pending_pkt(): Called\n")); mutex_enter(&instance->cmd_pend_mtx); mlist_del_init(&cmd->list); mlist_add_tail(&cmd->list, &instance->cmd_pend_list); if (cmd->sync_cmd == MRSAS_TRUE) { hdr = (struct mrsas_header *)&cmd->frame->hdr; if (hdr) { con_log(CL_ANN1, (CE_CONT, "push_pending_mfi_pkt: " "cmd %p index %x " "time %llx", (void *)cmd, cmd->index, gethrtime())); /* Wait for specified interval */ cmd->drv_pkt_time = ddi_get16( cmd->frame_dma_obj.acc_handle, &hdr->timeout); if (cmd->drv_pkt_time < debug_timeout_g) cmd->drv_pkt_time = (uint16_t)debug_timeout_g; con_log(CL_ANN1, (CE_CONT, "push_pending_pkt(): Called IO Timeout Value %x\n", cmd->drv_pkt_time)); } if (hdr && instance->timeout_id == (timeout_id_t)-1) { instance->timeout_id = timeout(io_timeout_checker, (void *) instance, drv_usectohz(MRSAS_1_SECOND)); } } else { pkt = cmd->pkt; if (pkt) { con_log(CL_ANN1, (CE_CONT, "push_pending_mfi_pkt: " "cmd %p index %x pkt %p, " "time %llx", (void *)cmd, cmd->index, (void *)pkt, gethrtime())); cmd->drv_pkt_time = (uint16_t)debug_timeout_g; } if (pkt && instance->timeout_id == (timeout_id_t)-1) { instance->timeout_id = timeout(io_timeout_checker, (void *) instance, drv_usectohz(MRSAS_1_SECOND)); } } mutex_exit(&instance->cmd_pend_mtx); } int mrsas_print_pending_cmds(struct mrsas_instance *instance) { mlist_t *head = &instance->cmd_pend_list; mlist_t *tmp = head; struct mrsas_cmd *cmd = NULL; struct mrsas_header *hdr; unsigned int flag = 1; struct scsi_pkt *pkt; int saved_level; int cmd_count = 0; saved_level = debug_level_g; debug_level_g = CL_ANN1; dev_err(instance->dip, CE_NOTE, "mrsas_print_pending_cmds(): Called"); while (flag) { mutex_enter(&instance->cmd_pend_mtx); tmp = tmp->next; if (tmp == head) { mutex_exit(&instance->cmd_pend_mtx); flag = 0; con_log(CL_ANN1, (CE_CONT, "mrsas_print_pending_cmds():" " NO MORE CMDS PENDING....\n")); break; } else { cmd = mlist_entry(tmp, struct mrsas_cmd, list); mutex_exit(&instance->cmd_pend_mtx); if (cmd) { if (cmd->sync_cmd == MRSAS_TRUE) { hdr = (struct mrsas_header *) &cmd->frame->hdr; if (hdr) { con_log(CL_ANN1, (CE_CONT, "print: cmd %p index 0x%x " "drv_pkt_time 0x%x (NO-PKT)" " hdr %p\n", (void *)cmd, cmd->index, cmd->drv_pkt_time, (void *)hdr)); } } else { pkt = cmd->pkt; if (pkt) { con_log(CL_ANN1, (CE_CONT, "print: cmd %p index 0x%x " "drv_pkt_time 0x%x pkt %p \n", (void *)cmd, cmd->index, cmd->drv_pkt_time, (void *)pkt)); } } if (++cmd_count == 1) { mrsas_print_cmd_details(instance, cmd, 0xDD); } else { mrsas_print_cmd_details(instance, cmd, 1); } } } } con_log(CL_ANN1, (CE_CONT, "mrsas_print_pending_cmds(): Done\n")); debug_level_g = saved_level; return (DDI_SUCCESS); } int mrsas_complete_pending_cmds(struct mrsas_instance *instance) { struct mrsas_cmd *cmd = NULL; struct scsi_pkt *pkt; struct mrsas_header *hdr; struct mlist_head *pos, *next; con_log(CL_ANN1, (CE_NOTE, "mrsas_complete_pending_cmds(): Called")); mutex_enter(&instance->cmd_pend_mtx); mlist_for_each_safe(pos, next, &instance->cmd_pend_list) { cmd = mlist_entry(pos, struct mrsas_cmd, list); if (cmd) { pkt = cmd->pkt; if (pkt) { /* for IO */ if (((pkt->pkt_flags & FLAG_NOINTR) == 0) && pkt->pkt_comp) { pkt->pkt_reason = CMD_DEV_GONE; pkt->pkt_statistics = STAT_DISCON; con_log(CL_ANN1, (CE_CONT, "fail and posting to scsa " "cmd %p index %x" " pkt %p " "time : %llx", (void *)cmd, cmd->index, (void *)pkt, gethrtime())); (*pkt->pkt_comp)(pkt); } } else { /* for DCMDS */ if (cmd->sync_cmd == MRSAS_TRUE) { hdr = (struct mrsas_header *)&cmd->frame->hdr; con_log(CL_ANN1, (CE_CONT, "posting invalid status to application " "cmd %p index %x" " hdr %p " "time : %llx", (void *)cmd, cmd->index, (void *)hdr, gethrtime())); hdr->cmd_status = MFI_STAT_INVALID_STATUS; complete_cmd_in_sync_mode(instance, cmd); } } mlist_del_init(&cmd->list); } else { con_log(CL_ANN1, (CE_CONT, "mrsas_complete_pending_cmds:" "NULL command\n")); } con_log(CL_ANN1, (CE_CONT, "mrsas_complete_pending_cmds:" "looping for more commands\n")); } mutex_exit(&instance->cmd_pend_mtx); con_log(CL_ANN1, (CE_CONT, "mrsas_complete_pending_cmds(): DONE\n")); return (DDI_SUCCESS); } void mrsas_print_cmd_details(struct mrsas_instance *instance, struct mrsas_cmd *cmd, int detail) { struct scsi_pkt *pkt = cmd->pkt; Mpi2RaidSCSIIORequest_t *scsi_io = cmd->scsi_io_request; int i; int saved_level; ddi_acc_handle_t acc_handle = instance->mpi2_frame_pool_dma_obj.acc_handle; if (detail == 0xDD) { saved_level = debug_level_g; debug_level_g = CL_ANN1; } if (instance->tbolt) { con_log(CL_ANN1, (CE_CONT, "print_cmd_details: cmd %p " "cmd->index 0x%x SMID 0x%x timer 0x%x sec\n", (void *)cmd, cmd->index, cmd->SMID, cmd->drv_pkt_time)); } else { con_log(CL_ANN1, (CE_CONT, "print_cmd_details: cmd %p " "cmd->index 0x%x timer 0x%x sec\n", (void *)cmd, cmd->index, cmd->drv_pkt_time)); } if (pkt) { con_log(CL_ANN1, (CE_CONT, "scsi_pkt CDB[0]=0x%x", pkt->pkt_cdbp[0])); } else { con_log(CL_ANN1, (CE_CONT, "NO-PKT")); } if ((detail == 0xDD) && instance->tbolt) { con_log(CL_ANN1, (CE_CONT, "RAID_SCSI_IO_REQUEST\n")); con_log(CL_ANN1, (CE_CONT, "DevHandle=0x%X Function=0x%X " "IoFlags=0x%X SGLFlags=0x%X DataLength=0x%X\n", ddi_get16(acc_handle, &scsi_io->DevHandle), ddi_get8(acc_handle, &scsi_io->Function), ddi_get16(acc_handle, &scsi_io->IoFlags), ddi_get16(acc_handle, &scsi_io->SGLFlags), ddi_get32(acc_handle, &scsi_io->DataLength))); for (i = 0; i < 32; i++) { con_log(CL_ANN1, (CE_CONT, "CDB[%d]=0x%x ", i, ddi_get8(acc_handle, &scsi_io->CDB.CDB32[i]))); } con_log(CL_ANN1, (CE_CONT, "RAID-CONTEXT\n")); con_log(CL_ANN1, (CE_CONT, "status=0x%X extStatus=0x%X " "ldTargetId=0x%X timeoutValue=0x%X regLockFlags=0x%X " "RAIDFlags=0x%X regLockRowLBA=0x%" PRIu64 " regLockLength=0x%X spanArm=0x%X\n", ddi_get8(acc_handle, &scsi_io->RaidContext.status), ddi_get8(acc_handle, &scsi_io->RaidContext.extStatus), ddi_get16(acc_handle, &scsi_io->RaidContext.ldTargetId), ddi_get16(acc_handle, &scsi_io->RaidContext.timeoutValue), ddi_get8(acc_handle, &scsi_io->RaidContext.regLockFlags), ddi_get8(acc_handle, &scsi_io->RaidContext.RAIDFlags), ddi_get64(acc_handle, &scsi_io->RaidContext.regLockRowLBA), ddi_get32(acc_handle, &scsi_io->RaidContext.regLockLength), ddi_get8(acc_handle, &scsi_io->RaidContext.spanArm))); } if (detail == 0xDD) { debug_level_g = saved_level; } } int mrsas_issue_pending_cmds(struct mrsas_instance *instance) { mlist_t *head = &instance->cmd_pend_list; mlist_t *tmp = head->next; struct mrsas_cmd *cmd = NULL; struct scsi_pkt *pkt; con_log(CL_ANN1, (CE_NOTE, "mrsas_issue_pending_cmds(): Called")); while (tmp != head) { mutex_enter(&instance->cmd_pend_mtx); cmd = mlist_entry(tmp, struct mrsas_cmd, list); tmp = tmp->next; mutex_exit(&instance->cmd_pend_mtx); if (cmd) { con_log(CL_ANN1, (CE_CONT, "mrsas_issue_pending_cmds(): " "Got a cmd: cmd %p index 0x%x drv_pkt_time 0x%x ", (void *)cmd, cmd->index, cmd->drv_pkt_time)); /* Reset command timeout value */ if (cmd->drv_pkt_time < debug_timeout_g) cmd->drv_pkt_time = (uint16_t)debug_timeout_g; cmd->retry_count_for_ocr++; dev_err(instance->dip, CE_CONT, "cmd retry count = %d\n", cmd->retry_count_for_ocr); if (cmd->retry_count_for_ocr > IO_RETRY_COUNT) { dev_err(instance->dip, CE_WARN, "mrsas_issue_pending_cmds(): " "cmd->retry_count exceeded limit >%d\n", IO_RETRY_COUNT); mrsas_print_cmd_details(instance, cmd, 0xDD); dev_err(instance->dip, CE_WARN, "mrsas_issue_pending_cmds():" "Calling KILL Adapter"); if (instance->tbolt) mrsas_tbolt_kill_adapter(instance); else (void) mrsas_kill_adapter(instance); return (DDI_FAILURE); } pkt = cmd->pkt; if (pkt) { con_log(CL_ANN1, (CE_CONT, "PENDING PKT-CMD ISSUE: cmd %p index %x " "pkt %p time %llx", (void *)cmd, cmd->index, (void *)pkt, gethrtime())); } else { dev_err(instance->dip, CE_CONT, "mrsas_issue_pending_cmds(): NO-PKT, " "cmd %p index 0x%x drv_pkt_time 0x%x", (void *)cmd, cmd->index, cmd->drv_pkt_time); } if (cmd->sync_cmd == MRSAS_TRUE) { dev_err(instance->dip, CE_CONT, "mrsas_issue_pending_cmds(): " "SYNC_CMD == TRUE \n"); instance->func_ptr->issue_cmd_in_sync_mode( instance, cmd); } else { instance->func_ptr->issue_cmd(cmd, instance); } } else { con_log(CL_ANN1, (CE_CONT, "mrsas_issue_pending_cmds: NULL command\n")); } con_log(CL_ANN1, (CE_CONT, "mrsas_issue_pending_cmds:" "looping for more commands")); } con_log(CL_ANN1, (CE_CONT, "mrsas_issue_pending_cmds(): DONE\n")); return (DDI_SUCCESS); } /* * destroy_mfi_frame_pool */ void destroy_mfi_frame_pool(struct mrsas_instance *instance) { int i; uint32_t max_cmd = instance->max_fw_cmds; struct mrsas_cmd *cmd; /* return all frames to pool */ for (i = 0; i < max_cmd; i++) { cmd = instance->cmd_list[i]; if (cmd->frame_dma_obj_status == DMA_OBJ_ALLOCATED) (void) mrsas_free_dma_obj(instance, cmd->frame_dma_obj); cmd->frame_dma_obj_status = DMA_OBJ_FREED; } } /* * create_mfi_frame_pool */ int create_mfi_frame_pool(struct mrsas_instance *instance) { int i = 0; int cookie_cnt; uint16_t max_cmd; uint16_t sge_sz; uint32_t sgl_sz; uint32_t tot_frame_size; struct mrsas_cmd *cmd; int retval = DDI_SUCCESS; max_cmd = instance->max_fw_cmds; sge_sz = sizeof (struct mrsas_sge_ieee); /* calculated the number of 64byte frames required for SGL */ sgl_sz = sge_sz * instance->max_num_sge; tot_frame_size = sgl_sz + MRMFI_FRAME_SIZE + SENSE_LENGTH; con_log(CL_DLEVEL3, (CE_NOTE, "create_mfi_frame_pool: " "sgl_sz %x tot_frame_size %x", sgl_sz, tot_frame_size)); while (i < max_cmd) { cmd = instance->cmd_list[i]; cmd->frame_dma_obj.size = tot_frame_size; cmd->frame_dma_obj.dma_attr = mrsas_generic_dma_attr; cmd->frame_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU; cmd->frame_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU; cmd->frame_dma_obj.dma_attr.dma_attr_sgllen = 1; cmd->frame_dma_obj.dma_attr.dma_attr_align = 64; cookie_cnt = mrsas_alloc_dma_obj(instance, &cmd->frame_dma_obj, (uchar_t)DDI_STRUCTURE_LE_ACC); if (cookie_cnt == -1 || cookie_cnt > 1) { dev_err(instance->dip, CE_WARN, "create_mfi_frame_pool: could not alloc."); retval = DDI_FAILURE; goto mrsas_undo_frame_pool; } bzero(cmd->frame_dma_obj.buffer, tot_frame_size); cmd->frame_dma_obj_status = DMA_OBJ_ALLOCATED; cmd->frame = (union mrsas_frame *)cmd->frame_dma_obj.buffer; cmd->frame_phys_addr = cmd->frame_dma_obj.dma_cookie[0].dmac_address; cmd->sense = (uint8_t *)(((unsigned long) cmd->frame_dma_obj.buffer) + tot_frame_size - SENSE_LENGTH); cmd->sense_phys_addr = cmd->frame_dma_obj.dma_cookie[0].dmac_address + tot_frame_size - SENSE_LENGTH; if (!cmd->frame || !cmd->sense) { dev_err(instance->dip, CE_WARN, "pci_pool_alloc failed"); retval = ENOMEM; goto mrsas_undo_frame_pool; } ddi_put32(cmd->frame_dma_obj.acc_handle, &cmd->frame->io.context, cmd->index); i++; con_log(CL_DLEVEL3, (CE_NOTE, "[%x]-%x", cmd->index, cmd->frame_phys_addr)); } return (DDI_SUCCESS); mrsas_undo_frame_pool: if (i > 0) destroy_mfi_frame_pool(instance); return (retval); } /* * free_additional_dma_buffer */ static void free_additional_dma_buffer(struct mrsas_instance *instance) { if (instance->mfi_internal_dma_obj.status == DMA_OBJ_ALLOCATED) { (void) mrsas_free_dma_obj(instance, instance->mfi_internal_dma_obj); instance->mfi_internal_dma_obj.status = DMA_OBJ_FREED; } if (instance->mfi_evt_detail_obj.status == DMA_OBJ_ALLOCATED) { (void) mrsas_free_dma_obj(instance, instance->mfi_evt_detail_obj); instance->mfi_evt_detail_obj.status = DMA_OBJ_FREED; } } /* * alloc_additional_dma_buffer */ static int alloc_additional_dma_buffer(struct mrsas_instance *instance) { uint32_t reply_q_sz; uint32_t internal_buf_size = PAGESIZE*2; /* max cmds plus 1 + producer & consumer */ reply_q_sz = sizeof (uint32_t) * (instance->max_fw_cmds + 1 + 2); instance->mfi_internal_dma_obj.size = internal_buf_size; instance->mfi_internal_dma_obj.dma_attr = mrsas_generic_dma_attr; instance->mfi_internal_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU; instance->mfi_internal_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU; instance->mfi_internal_dma_obj.dma_attr.dma_attr_sgllen = 1; if (mrsas_alloc_dma_obj(instance, &instance->mfi_internal_dma_obj, (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) { dev_err(instance->dip, CE_WARN, "could not alloc reply queue"); return (DDI_FAILURE); } bzero(instance->mfi_internal_dma_obj.buffer, internal_buf_size); instance->mfi_internal_dma_obj.status |= DMA_OBJ_ALLOCATED; instance->producer = (uint32_t *)((unsigned long) instance->mfi_internal_dma_obj.buffer); instance->consumer = (uint32_t *)((unsigned long) instance->mfi_internal_dma_obj.buffer + 4); instance->reply_queue = (uint32_t *)((unsigned long) instance->mfi_internal_dma_obj.buffer + 8); instance->internal_buf = (caddr_t)(((unsigned long) instance->mfi_internal_dma_obj.buffer) + reply_q_sz + 8); instance->internal_buf_dmac_add = instance->mfi_internal_dma_obj.dma_cookie[0].dmac_address + (reply_q_sz + 8); instance->internal_buf_size = internal_buf_size - (reply_q_sz + 8); /* allocate evt_detail */ instance->mfi_evt_detail_obj.size = sizeof (struct mrsas_evt_detail); instance->mfi_evt_detail_obj.dma_attr = mrsas_generic_dma_attr; instance->mfi_evt_detail_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU; instance->mfi_evt_detail_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU; instance->mfi_evt_detail_obj.dma_attr.dma_attr_sgllen = 1; instance->mfi_evt_detail_obj.dma_attr.dma_attr_align = 1; if (mrsas_alloc_dma_obj(instance, &instance->mfi_evt_detail_obj, (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) { dev_err(instance->dip, CE_WARN, "alloc_additional_dma_buffer: " "could not allocate data transfer buffer."); goto mrsas_undo_internal_buff; } bzero(instance->mfi_evt_detail_obj.buffer, sizeof (struct mrsas_evt_detail)); instance->mfi_evt_detail_obj.status |= DMA_OBJ_ALLOCATED; return (DDI_SUCCESS); mrsas_undo_internal_buff: if (instance->mfi_internal_dma_obj.status == DMA_OBJ_ALLOCATED) { (void) mrsas_free_dma_obj(instance, instance->mfi_internal_dma_obj); instance->mfi_internal_dma_obj.status = DMA_OBJ_FREED; } return (DDI_FAILURE); } void mrsas_free_cmd_pool(struct mrsas_instance *instance) { int i; uint32_t max_cmd; size_t sz; /* already freed */ if (instance->cmd_list == NULL) { return; } max_cmd = instance->max_fw_cmds; /* size of cmd_list array */ sz = sizeof (struct mrsas_cmd *) * max_cmd; /* First free each cmd */ for (i = 0; i < max_cmd; i++) { if (instance->cmd_list[i] != NULL) { kmem_free(instance->cmd_list[i], sizeof (struct mrsas_cmd)); } instance->cmd_list[i] = NULL; } /* Now, free cmd_list array */ if (instance->cmd_list != NULL) kmem_free(instance->cmd_list, sz); instance->cmd_list = NULL; INIT_LIST_HEAD(&instance->cmd_pool_list); INIT_LIST_HEAD(&instance->cmd_pend_list); if (instance->tbolt) { INIT_LIST_HEAD(&instance->cmd_app_pool_list); } else { INIT_LIST_HEAD(&instance->app_cmd_pool_list); } } /* * mrsas_alloc_cmd_pool */ int mrsas_alloc_cmd_pool(struct mrsas_instance *instance) { int i; int count; uint32_t max_cmd; uint32_t reserve_cmd; size_t sz; struct mrsas_cmd *cmd; max_cmd = instance->max_fw_cmds; con_log(CL_ANN1, (CE_NOTE, "mrsas_alloc_cmd_pool: " "max_cmd %x", max_cmd)); sz = sizeof (struct mrsas_cmd *) * max_cmd; /* * instance->cmd_list is an array of struct mrsas_cmd pointers. * Allocate the dynamic array first and then allocate individual * commands. */ instance->cmd_list = kmem_zalloc(sz, KM_SLEEP); ASSERT(instance->cmd_list); /* create a frame pool and assign one frame to each cmd */ for (count = 0; count < max_cmd; count++) { instance->cmd_list[count] = kmem_zalloc(sizeof (struct mrsas_cmd), KM_SLEEP); ASSERT(instance->cmd_list[count]); } /* add all the commands to command pool */ INIT_LIST_HEAD(&instance->cmd_pool_list); INIT_LIST_HEAD(&instance->cmd_pend_list); INIT_LIST_HEAD(&instance->app_cmd_pool_list); /* * When max_cmd is lower than MRSAS_APP_RESERVED_CMDS, how do I split * into app_cmd and regular cmd? For now, just take * max(1/8th of max, 4); */ reserve_cmd = min(MRSAS_APP_RESERVED_CMDS, max(max_cmd >> 3, MRSAS_APP_MIN_RESERVED_CMDS)); for (i = 0; i < reserve_cmd; i++) { cmd = instance->cmd_list[i]; cmd->index = i; mlist_add_tail(&cmd->list, &instance->app_cmd_pool_list); } for (i = reserve_cmd; i < max_cmd; i++) { cmd = instance->cmd_list[i]; cmd->index = i; mlist_add_tail(&cmd->list, &instance->cmd_pool_list); } return (DDI_SUCCESS); mrsas_undo_cmds: if (count > 0) { /* free each cmd */ for (i = 0; i < count; i++) { if (instance->cmd_list[i] != NULL) { kmem_free(instance->cmd_list[i], sizeof (struct mrsas_cmd)); } instance->cmd_list[i] = NULL; } } mrsas_undo_cmd_list: if (instance->cmd_list != NULL) kmem_free(instance->cmd_list, sz); instance->cmd_list = NULL; return (DDI_FAILURE); } /* * free_space_for_mfi */ static void free_space_for_mfi(struct mrsas_instance *instance) { /* already freed */ if (instance->cmd_list == NULL) { return; } /* Free additional dma buffer */ free_additional_dma_buffer(instance); /* Free the MFI frame pool */ destroy_mfi_frame_pool(instance); /* Free all the commands in the cmd_list */ /* Free the cmd_list buffer itself */ mrsas_free_cmd_pool(instance); } /* * alloc_space_for_mfi */ static int alloc_space_for_mfi(struct mrsas_instance *instance) { /* Allocate command pool (memory for cmd_list & individual commands) */ if (mrsas_alloc_cmd_pool(instance)) { dev_err(instance->dip, CE_WARN, "error creating cmd pool"); return (DDI_FAILURE); } /* Allocate MFI Frame pool */ if (create_mfi_frame_pool(instance)) { dev_err(instance->dip, CE_WARN, "error creating frame DMA pool"); goto mfi_undo_cmd_pool; } /* Allocate additional DMA buffer */ if (alloc_additional_dma_buffer(instance)) { dev_err(instance->dip, CE_WARN, "error creating frame DMA pool"); goto mfi_undo_frame_pool; } return (DDI_SUCCESS); mfi_undo_frame_pool: destroy_mfi_frame_pool(instance); mfi_undo_cmd_pool: mrsas_free_cmd_pool(instance); return (DDI_FAILURE); } /* * get_ctrl_info */ static int get_ctrl_info(struct mrsas_instance *instance, struct mrsas_ctrl_info *ctrl_info) { int ret = 0; struct mrsas_cmd *cmd; struct mrsas_dcmd_frame *dcmd; struct mrsas_ctrl_info *ci; if (instance->tbolt) { cmd = get_raid_msg_mfi_pkt(instance); } else { cmd = mrsas_get_mfi_pkt(instance); } if (!cmd) { con_log(CL_ANN, (CE_WARN, "Failed to get a cmd for ctrl info")); DTRACE_PROBE2(info_mfi_err, uint16_t, instance->fw_outstanding, uint16_t, instance->max_fw_cmds); return (DDI_FAILURE); } /* Clear the frame buffer and assign back the context id */ (void) memset((char *)&cmd->frame[0], 0, sizeof (union mrsas_frame)); ddi_put32(cmd->frame_dma_obj.acc_handle, &cmd->frame->hdr.context, cmd->index); dcmd = &cmd->frame->dcmd; ci = (struct mrsas_ctrl_info *)instance->internal_buf; if (!ci) { dev_err(instance->dip, CE_WARN, "Failed to alloc mem for ctrl info"); mrsas_return_mfi_pkt(instance, cmd); return (DDI_FAILURE); } (void) memset(ci, 0, sizeof (struct mrsas_ctrl_info)); /* for( i = 0; i < DCMD_MBOX_SZ; i++ ) dcmd->mbox.b[i] = 0; */ (void) memset(dcmd->mbox.b, 0, DCMD_MBOX_SZ); ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->cmd, MFI_CMD_OP_DCMD); ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->cmd_status, MFI_CMD_STATUS_POLL_MODE); ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->sge_count, 1); ddi_put16(cmd->frame_dma_obj.acc_handle, &dcmd->flags, MFI_FRAME_DIR_READ); ddi_put16(cmd->frame_dma_obj.acc_handle, &dcmd->timeout, 0); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->data_xfer_len, sizeof (struct mrsas_ctrl_info)); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->opcode, MR_DCMD_CTRL_GET_INFO); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->sgl.sge32[0].phys_addr, instance->internal_buf_dmac_add); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->sgl.sge32[0].length, sizeof (struct mrsas_ctrl_info)); cmd->frame_count = 1; if (instance->tbolt) { mr_sas_tbolt_build_mfi_cmd(instance, cmd); } if (!instance->func_ptr->issue_cmd_in_poll_mode(instance, cmd)) { ret = 0; ctrl_info->max_request_size = ddi_get32( cmd->frame_dma_obj.acc_handle, &ci->max_request_size); ctrl_info->ld_present_count = ddi_get16( cmd->frame_dma_obj.acc_handle, &ci->ld_present_count); ctrl_info->properties.on_off_properties = ddi_get32( cmd->frame_dma_obj.acc_handle, &ci->properties.on_off_properties); ddi_rep_get8(cmd->frame_dma_obj.acc_handle, (uint8_t *)(ctrl_info->product_name), (uint8_t *)(ci->product_name), 80 * sizeof (char), DDI_DEV_AUTOINCR); /* should get more members of ci with ddi_get when needed */ } else { dev_err(instance->dip, CE_WARN, "get_ctrl_info: Ctrl info failed"); ret = -1; } if (mrsas_common_check(instance, cmd) != DDI_SUCCESS) { ret = -1; } if (instance->tbolt) { return_raid_msg_mfi_pkt(instance, cmd); } else { mrsas_return_mfi_pkt(instance, cmd); } return (ret); } /* * abort_aen_cmd */ static int abort_aen_cmd(struct mrsas_instance *instance, struct mrsas_cmd *cmd_to_abort) { int ret = 0; struct mrsas_cmd *cmd; struct mrsas_abort_frame *abort_fr; con_log(CL_ANN1, (CE_NOTE, "chkpnt: abort_aen:%d", __LINE__)); if (instance->tbolt) { cmd = get_raid_msg_mfi_pkt(instance); } else { cmd = mrsas_get_mfi_pkt(instance); } if (!cmd) { con_log(CL_ANN1, (CE_WARN, "abort_aen_cmd():Failed to get a cmd for abort_aen_cmd")); DTRACE_PROBE2(abort_mfi_err, uint16_t, instance->fw_outstanding, uint16_t, instance->max_fw_cmds); return (DDI_FAILURE); } /* Clear the frame buffer and assign back the context id */ (void) memset((char *)&cmd->frame[0], 0, sizeof (union mrsas_frame)); ddi_put32(cmd->frame_dma_obj.acc_handle, &cmd->frame->hdr.context, cmd->index); abort_fr = &cmd->frame->abort; /* prepare and issue the abort frame */ ddi_put8(cmd->frame_dma_obj.acc_handle, &abort_fr->cmd, MFI_CMD_OP_ABORT); ddi_put8(cmd->frame_dma_obj.acc_handle, &abort_fr->cmd_status, MFI_CMD_STATUS_SYNC_MODE); ddi_put16(cmd->frame_dma_obj.acc_handle, &abort_fr->flags, 0); ddi_put32(cmd->frame_dma_obj.acc_handle, &abort_fr->abort_context, cmd_to_abort->index); ddi_put32(cmd->frame_dma_obj.acc_handle, &abort_fr->abort_mfi_phys_addr_lo, cmd_to_abort->frame_phys_addr); ddi_put32(cmd->frame_dma_obj.acc_handle, &abort_fr->abort_mfi_phys_addr_hi, 0); instance->aen_cmd->abort_aen = 1; cmd->frame_count = 1; if (instance->tbolt) { mr_sas_tbolt_build_mfi_cmd(instance, cmd); } if (instance->func_ptr->issue_cmd_in_poll_mode(instance, cmd)) { con_log(CL_ANN1, (CE_WARN, "abort_aen_cmd: issue_cmd_in_poll_mode failed")); ret = -1; } else { ret = 0; } instance->aen_cmd->abort_aen = 1; instance->aen_cmd = 0; if (instance->tbolt) { return_raid_msg_mfi_pkt(instance, cmd); } else { mrsas_return_mfi_pkt(instance, cmd); } atomic_add_16(&instance->fw_outstanding, (-1)); return (ret); } static int mrsas_build_init_cmd(struct mrsas_instance *instance, struct mrsas_cmd **cmd_ptr) { struct mrsas_cmd *cmd; struct mrsas_init_frame *init_frame; struct mrsas_init_queue_info *initq_info; struct mrsas_drv_ver drv_ver_info; /* * Prepare a init frame. Note the init frame points to queue info * structure. Each frame has SGL allocated after first 64 bytes. For * this frame - since we don't need any SGL - we use SGL's space as * queue info structure */ cmd = *cmd_ptr; /* Clear the frame buffer and assign back the context id */ (void) memset((char *)&cmd->frame[0], 0, sizeof (union mrsas_frame)); ddi_put32(cmd->frame_dma_obj.acc_handle, &cmd->frame->hdr.context, cmd->index); init_frame = (struct mrsas_init_frame *)cmd->frame; initq_info = (struct mrsas_init_queue_info *) ((unsigned long)init_frame + 64); (void) memset(init_frame, 0, MRMFI_FRAME_SIZE); (void) memset(initq_info, 0, sizeof (struct mrsas_init_queue_info)); ddi_put32(cmd->frame_dma_obj.acc_handle, &initq_info->init_flags, 0); ddi_put32(cmd->frame_dma_obj.acc_handle, &initq_info->reply_queue_entries, instance->max_fw_cmds + 1); ddi_put32(cmd->frame_dma_obj.acc_handle, &initq_info->producer_index_phys_addr_hi, 0); ddi_put32(cmd->frame_dma_obj.acc_handle, &initq_info->producer_index_phys_addr_lo, instance->mfi_internal_dma_obj.dma_cookie[0].dmac_address); ddi_put32(cmd->frame_dma_obj.acc_handle, &initq_info->consumer_index_phys_addr_hi, 0); ddi_put32(cmd->frame_dma_obj.acc_handle, &initq_info->consumer_index_phys_addr_lo, instance->mfi_internal_dma_obj.dma_cookie[0].dmac_address + 4); ddi_put32(cmd->frame_dma_obj.acc_handle, &initq_info->reply_queue_start_phys_addr_hi, 0); ddi_put32(cmd->frame_dma_obj.acc_handle, &initq_info->reply_queue_start_phys_addr_lo, instance->mfi_internal_dma_obj.dma_cookie[0].dmac_address + 8); ddi_put8(cmd->frame_dma_obj.acc_handle, &init_frame->cmd, MFI_CMD_OP_INIT); ddi_put8(cmd->frame_dma_obj.acc_handle, &init_frame->cmd_status, MFI_CMD_STATUS_POLL_MODE); ddi_put16(cmd->frame_dma_obj.acc_handle, &init_frame->flags, 0); ddi_put32(cmd->frame_dma_obj.acc_handle, &init_frame->queue_info_new_phys_addr_lo, cmd->frame_phys_addr + 64); ddi_put32(cmd->frame_dma_obj.acc_handle, &init_frame->queue_info_new_phys_addr_hi, 0); /* fill driver version information */ fill_up_drv_ver(&drv_ver_info); /* allocate the driver version data transfer buffer */ instance->drv_ver_dma_obj.size = sizeof (drv_ver_info.drv_ver); instance->drv_ver_dma_obj.dma_attr = mrsas_generic_dma_attr; instance->drv_ver_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU; instance->drv_ver_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU; instance->drv_ver_dma_obj.dma_attr.dma_attr_sgllen = 1; instance->drv_ver_dma_obj.dma_attr.dma_attr_align = 1; if (mrsas_alloc_dma_obj(instance, &instance->drv_ver_dma_obj, (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) { con_log(CL_ANN, (CE_WARN, "init_mfi : Could not allocate driver version buffer.")); return (DDI_FAILURE); } /* copy driver version to dma buffer */ (void) memset(instance->drv_ver_dma_obj.buffer, 0, sizeof (drv_ver_info.drv_ver)); ddi_rep_put8(cmd->frame_dma_obj.acc_handle, (uint8_t *)drv_ver_info.drv_ver, (uint8_t *)instance->drv_ver_dma_obj.buffer, sizeof (drv_ver_info.drv_ver), DDI_DEV_AUTOINCR); /* copy driver version physical address to init frame */ ddi_put64(cmd->frame_dma_obj.acc_handle, &init_frame->driverversion, instance->drv_ver_dma_obj.dma_cookie[0].dmac_address); ddi_put32(cmd->frame_dma_obj.acc_handle, &init_frame->data_xfer_len, sizeof (struct mrsas_init_queue_info)); cmd->frame_count = 1; *cmd_ptr = cmd; return (DDI_SUCCESS); } /* * mrsas_init_adapter_ppc - Initialize MFI interface adapter. */ int mrsas_init_adapter_ppc(struct mrsas_instance *instance) { struct mrsas_cmd *cmd; /* * allocate memory for mfi adapter(cmd pool, individual commands, mfi * frames etc */ if (alloc_space_for_mfi(instance) != DDI_SUCCESS) { con_log(CL_ANN, (CE_NOTE, "Error, failed to allocate memory for MFI adapter")); return (DDI_FAILURE); } /* Build INIT command */ cmd = mrsas_get_mfi_pkt(instance); if (cmd == NULL) { DTRACE_PROBE2(init_adapter_mfi_err, uint16_t, instance->fw_outstanding, uint16_t, instance->max_fw_cmds); return (DDI_FAILURE); } if (mrsas_build_init_cmd(instance, &cmd) != DDI_SUCCESS) { con_log(CL_ANN, (CE_NOTE, "Error, failed to build INIT command")); goto fail_undo_alloc_mfi_space; } /* * Disable interrupt before sending init frame ( see linux driver code) * send INIT MFI frame in polled mode */ if (instance->func_ptr->issue_cmd_in_poll_mode(instance, cmd)) { con_log(CL_ANN, (CE_WARN, "failed to init firmware")); goto fail_fw_init; } if (mrsas_common_check(instance, cmd) != DDI_SUCCESS) goto fail_fw_init; mrsas_return_mfi_pkt(instance, cmd); if (ctio_enable && (instance->func_ptr->read_fw_status_reg(instance) & 0x04000000)) { con_log(CL_ANN, (CE_NOTE, "mr_sas: IEEE SGL's supported")); instance->flag_ieee = 1; } else { instance->flag_ieee = 0; } ASSERT(!instance->skinny || instance->flag_ieee); instance->unroll.alloc_space_mfi = 1; instance->unroll.verBuff = 1; return (DDI_SUCCESS); fail_fw_init: (void) mrsas_free_dma_obj(instance, instance->drv_ver_dma_obj); fail_undo_alloc_mfi_space: mrsas_return_mfi_pkt(instance, cmd); free_space_for_mfi(instance); return (DDI_FAILURE); } /* * mrsas_init_adapter - Initialize adapter. */ int mrsas_init_adapter(struct mrsas_instance *instance) { struct mrsas_ctrl_info ctrl_info; /* we expect the FW state to be READY */ if (mfi_state_transition_to_ready(instance)) { con_log(CL_ANN, (CE_WARN, "mr_sas: F/W is not ready")); return (DDI_FAILURE); } /* get various operational parameters from status register */ instance->max_num_sge = (instance->func_ptr->read_fw_status_reg(instance) & 0xFF0000) >> 0x10; instance->max_num_sge = (instance->max_num_sge > MRSAS_MAX_SGE_CNT) ? MRSAS_MAX_SGE_CNT : instance->max_num_sge; /* * Reduce the max supported cmds by 1. This is to ensure that the * reply_q_sz (1 more than the max cmd that driver may send) * does not exceed max cmds that the FW can support */ instance->max_fw_cmds = instance->func_ptr->read_fw_status_reg(instance) & 0xFFFF; instance->max_fw_cmds = instance->max_fw_cmds - 1; /* Initialize adapter */ if (instance->func_ptr->init_adapter(instance) != DDI_SUCCESS) { con_log(CL_ANN, (CE_WARN, "mr_sas: could not initialize adapter")); return (DDI_FAILURE); } /* gather misc FW related information */ instance->disable_online_ctrl_reset = 0; if (!get_ctrl_info(instance, &ctrl_info)) { instance->max_sectors_per_req = ctrl_info.max_request_size; con_log(CL_ANN1, (CE_NOTE, "product name %s ld present %d", ctrl_info.product_name, ctrl_info.ld_present_count)); } else { instance->max_sectors_per_req = instance->max_num_sge * PAGESIZE / 512; } if (ctrl_info.properties.on_off_properties & DISABLE_OCR_PROP_FLAG) instance->disable_online_ctrl_reset = 1; return (DDI_SUCCESS); } static int mrsas_issue_init_mfi(struct mrsas_instance *instance) { struct mrsas_cmd *cmd; struct mrsas_init_frame *init_frame; struct mrsas_init_queue_info *initq_info; /* * Prepare a init frame. Note the init frame points to queue info * structure. Each frame has SGL allocated after first 64 bytes. For * this frame - since we don't need any SGL - we use SGL's space as * queue info structure */ con_log(CL_ANN1, (CE_NOTE, "mrsas_issue_init_mfi: entry\n")); cmd = get_mfi_app_pkt(instance); if (!cmd) { con_log(CL_ANN1, (CE_WARN, "mrsas_issue_init_mfi: get_pkt failed\n")); return (DDI_FAILURE); } /* Clear the frame buffer and assign back the context id */ (void) memset((char *)&cmd->frame[0], 0, sizeof (union mrsas_frame)); ddi_put32(cmd->frame_dma_obj.acc_handle, &cmd->frame->hdr.context, cmd->index); init_frame = (struct mrsas_init_frame *)cmd->frame; initq_info = (struct mrsas_init_queue_info *) ((unsigned long)init_frame + 64); (void) memset(init_frame, 0, MRMFI_FRAME_SIZE); (void) memset(initq_info, 0, sizeof (struct mrsas_init_queue_info)); ddi_put32(cmd->frame_dma_obj.acc_handle, &initq_info->init_flags, 0); ddi_put32(cmd->frame_dma_obj.acc_handle, &initq_info->reply_queue_entries, instance->max_fw_cmds + 1); ddi_put32(cmd->frame_dma_obj.acc_handle, &initq_info->producer_index_phys_addr_hi, 0); ddi_put32(cmd->frame_dma_obj.acc_handle, &initq_info->producer_index_phys_addr_lo, instance->mfi_internal_dma_obj.dma_cookie[0].dmac_address); ddi_put32(cmd->frame_dma_obj.acc_handle, &initq_info->consumer_index_phys_addr_hi, 0); ddi_put32(cmd->frame_dma_obj.acc_handle, &initq_info->consumer_index_phys_addr_lo, instance->mfi_internal_dma_obj.dma_cookie[0].dmac_address + 4); ddi_put32(cmd->frame_dma_obj.acc_handle, &initq_info->reply_queue_start_phys_addr_hi, 0); ddi_put32(cmd->frame_dma_obj.acc_handle, &initq_info->reply_queue_start_phys_addr_lo, instance->mfi_internal_dma_obj.dma_cookie[0].dmac_address + 8); ddi_put8(cmd->frame_dma_obj.acc_handle, &init_frame->cmd, MFI_CMD_OP_INIT); ddi_put8(cmd->frame_dma_obj.acc_handle, &init_frame->cmd_status, MFI_CMD_STATUS_POLL_MODE); ddi_put16(cmd->frame_dma_obj.acc_handle, &init_frame->flags, 0); ddi_put32(cmd->frame_dma_obj.acc_handle, &init_frame->queue_info_new_phys_addr_lo, cmd->frame_phys_addr + 64); ddi_put32(cmd->frame_dma_obj.acc_handle, &init_frame->queue_info_new_phys_addr_hi, 0); ddi_put32(cmd->frame_dma_obj.acc_handle, &init_frame->data_xfer_len, sizeof (struct mrsas_init_queue_info)); cmd->frame_count = 1; /* issue the init frame in polled mode */ if (instance->func_ptr->issue_cmd_in_poll_mode(instance, cmd)) { con_log(CL_ANN1, (CE_WARN, "mrsas_issue_init_mfi():failed to " "init firmware")); return_mfi_app_pkt(instance, cmd); return (DDI_FAILURE); } if (mrsas_common_check(instance, cmd) != DDI_SUCCESS) { return_mfi_app_pkt(instance, cmd); return (DDI_FAILURE); } return_mfi_app_pkt(instance, cmd); con_log(CL_ANN1, (CE_CONT, "mrsas_issue_init_mfi: Done")); return (DDI_SUCCESS); } /* * mfi_state_transition_to_ready : Move the FW to READY state * * @reg_set : MFI register set */ int mfi_state_transition_to_ready(struct mrsas_instance *instance) { int i; uint8_t max_wait; uint32_t fw_ctrl = 0; uint32_t fw_state; uint32_t cur_state; uint32_t cur_abs_reg_val; uint32_t prev_abs_reg_val; uint32_t status; cur_abs_reg_val = instance->func_ptr->read_fw_status_reg(instance); fw_state = cur_abs_reg_val & MFI_STATE_MASK; con_log(CL_ANN1, (CE_CONT, "mfi_state_transition_to_ready:FW state = 0x%x", fw_state)); while (fw_state != MFI_STATE_READY) { con_log(CL_ANN, (CE_CONT, "mfi_state_transition_to_ready:FW state%x", fw_state)); switch (fw_state) { case MFI_STATE_FAULT: con_log(CL_ANN, (CE_NOTE, "mr_sas: FW in FAULT state!!")); return (ENODEV); case MFI_STATE_WAIT_HANDSHAKE: /* set the CLR bit in IMR0 */ con_log(CL_ANN1, (CE_NOTE, "mr_sas: FW waiting for HANDSHAKE")); /* * PCI_Hot Plug: MFI F/W requires * (MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG) * to be set */ /* WR_IB_MSG_0(MFI_INIT_CLEAR_HANDSHAKE, instance); */ if (!instance->tbolt && !instance->skinny) { WR_IB_DOORBELL(MFI_INIT_CLEAR_HANDSHAKE | MFI_INIT_HOTPLUG, instance); } else { WR_RESERVED0_REGISTER(MFI_INIT_CLEAR_HANDSHAKE | MFI_INIT_HOTPLUG, instance); } max_wait = (instance->tbolt == 1) ? 180 : 2; cur_state = MFI_STATE_WAIT_HANDSHAKE; break; case MFI_STATE_BOOT_MESSAGE_PENDING: /* set the CLR bit in IMR0 */ con_log(CL_ANN1, (CE_NOTE, "mr_sas: FW state boot message pending")); /* * PCI_Hot Plug: MFI F/W requires * (MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG) * to be set */ if (!instance->tbolt && !instance->skinny) { WR_IB_DOORBELL(MFI_INIT_HOTPLUG, instance); } else { WR_RESERVED0_REGISTER(MFI_INIT_HOTPLUG, instance); } max_wait = (instance->tbolt == 1) ? 180 : 10; cur_state = MFI_STATE_BOOT_MESSAGE_PENDING; break; case MFI_STATE_OPERATIONAL: /* bring it to READY state; assuming max wait 2 secs */ instance->func_ptr->disable_intr(instance); con_log(CL_ANN1, (CE_NOTE, "mr_sas: FW in OPERATIONAL state")); /* * PCI_Hot Plug: MFI F/W requires * (MFI_INIT_READY | MFI_INIT_MFIMODE | MFI_INIT_ABORT) * to be set */ /* WR_IB_DOORBELL(MFI_INIT_READY, instance); */ if (!instance->tbolt && !instance->skinny) { WR_IB_DOORBELL(MFI_RESET_FLAGS, instance); } else { WR_RESERVED0_REGISTER(MFI_RESET_FLAGS, instance); for (i = 0; i < (10 * 1000); i++) { status = RD_RESERVED0_REGISTER(instance); if (status & 1) { delay(1 * drv_usectohz(MILLISEC)); } else { break; } } } max_wait = (instance->tbolt == 1) ? 180 : 10; cur_state = MFI_STATE_OPERATIONAL; break; case MFI_STATE_UNDEFINED: /* this state should not last for more than 2 seconds */ con_log(CL_ANN1, (CE_NOTE, "FW state undefined")); max_wait = (instance->tbolt == 1) ? 180 : 2; cur_state = MFI_STATE_UNDEFINED; break; case MFI_STATE_BB_INIT: max_wait = (instance->tbolt == 1) ? 180 : 2; cur_state = MFI_STATE_BB_INIT; break; case MFI_STATE_FW_INIT: max_wait = (instance->tbolt == 1) ? 180 : 2; cur_state = MFI_STATE_FW_INIT; break; case MFI_STATE_FW_INIT_2: max_wait = 180; cur_state = MFI_STATE_FW_INIT_2; break; case MFI_STATE_DEVICE_SCAN: max_wait = 180; cur_state = MFI_STATE_DEVICE_SCAN; prev_abs_reg_val = cur_abs_reg_val; con_log(CL_NONE, (CE_NOTE, "Device scan in progress ...\n")); break; case MFI_STATE_FLUSH_CACHE: max_wait = 180; cur_state = MFI_STATE_FLUSH_CACHE; break; default: con_log(CL_ANN1, (CE_NOTE, "mr_sas: Unknown state 0x%x", fw_state)); return (ENODEV); } /* the cur_state should not last for more than max_wait secs */ for (i = 0; i < (max_wait * MILLISEC); i++) { /* fw_state = RD_OB_MSG_0(instance) & MFI_STATE_MASK; */ cur_abs_reg_val = instance->func_ptr->read_fw_status_reg(instance); fw_state = cur_abs_reg_val & MFI_STATE_MASK; if (fw_state == cur_state) { delay(1 * drv_usectohz(MILLISEC)); } else { break; } } if (fw_state == MFI_STATE_DEVICE_SCAN) { if (prev_abs_reg_val != cur_abs_reg_val) { continue; } } /* return error if fw_state hasn't changed after max_wait */ if (fw_state == cur_state) { con_log(CL_ANN1, (CE_WARN, "FW state hasn't changed in %d secs", max_wait)); return (ENODEV); } }; /* This may also need to apply to Skinny, but for now, don't worry. */ if (!instance->tbolt && !instance->skinny) { fw_ctrl = RD_IB_DOORBELL(instance); con_log(CL_ANN1, (CE_CONT, "mfi_state_transition_to_ready:FW ctrl = 0x%x", fw_ctrl)); /* * Write 0xF to the doorbell register to do the following. * - Abort all outstanding commands (bit 0). * - Transition from OPERATIONAL to READY state (bit 1). * - Discard (possible) low MFA posted in 64-bit mode (bit-2). * - Set to release FW to continue running (i.e. BIOS handshake * (bit 3). */ WR_IB_DOORBELL(0xF, instance); } if (mrsas_check_acc_handle(instance->regmap_handle) != DDI_SUCCESS) { return (EIO); } return (DDI_SUCCESS); } /* * get_seq_num */ static int get_seq_num(struct mrsas_instance *instance, struct mrsas_evt_log_info *eli) { int ret = DDI_SUCCESS; dma_obj_t dcmd_dma_obj; struct mrsas_cmd *cmd; struct mrsas_dcmd_frame *dcmd; struct mrsas_evt_log_info *eli_tmp; if (instance->tbolt) { cmd = get_raid_msg_mfi_pkt(instance); } else { cmd = mrsas_get_mfi_pkt(instance); } if (!cmd) { dev_err(instance->dip, CE_WARN, "failed to get a cmd"); DTRACE_PROBE2(seq_num_mfi_err, uint16_t, instance->fw_outstanding, uint16_t, instance->max_fw_cmds); return (ENOMEM); } /* Clear the frame buffer and assign back the context id */ (void) memset((char *)&cmd->frame[0], 0, sizeof (union mrsas_frame)); ddi_put32(cmd->frame_dma_obj.acc_handle, &cmd->frame->hdr.context, cmd->index); dcmd = &cmd->frame->dcmd; /* allocate the data transfer buffer */ dcmd_dma_obj.size = sizeof (struct mrsas_evt_log_info); dcmd_dma_obj.dma_attr = mrsas_generic_dma_attr; dcmd_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU; dcmd_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU; dcmd_dma_obj.dma_attr.dma_attr_sgllen = 1; dcmd_dma_obj.dma_attr.dma_attr_align = 1; if (mrsas_alloc_dma_obj(instance, &dcmd_dma_obj, (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) { dev_err(instance->dip, CE_WARN, "get_seq_num: could not allocate data transfer buffer."); return (DDI_FAILURE); } (void) memset(dcmd_dma_obj.buffer, 0, sizeof (struct mrsas_evt_log_info)); (void) memset(dcmd->mbox.b, 0, DCMD_MBOX_SZ); ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->cmd, MFI_CMD_OP_DCMD); ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->cmd_status, 0); ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->sge_count, 1); ddi_put16(cmd->frame_dma_obj.acc_handle, &dcmd->flags, MFI_FRAME_DIR_READ); ddi_put16(cmd->frame_dma_obj.acc_handle, &dcmd->timeout, 0); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->data_xfer_len, sizeof (struct mrsas_evt_log_info)); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->opcode, MR_DCMD_CTRL_EVENT_GET_INFO); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->sgl.sge32[0].length, sizeof (struct mrsas_evt_log_info)); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->sgl.sge32[0].phys_addr, dcmd_dma_obj.dma_cookie[0].dmac_address); cmd->sync_cmd = MRSAS_TRUE; cmd->frame_count = 1; if (instance->tbolt) { mr_sas_tbolt_build_mfi_cmd(instance, cmd); } if (instance->func_ptr->issue_cmd_in_sync_mode(instance, cmd)) { dev_err(instance->dip, CE_WARN, "get_seq_num: " "failed to issue MRSAS_DCMD_CTRL_EVENT_GET_INFO"); ret = DDI_FAILURE; } else { eli_tmp = (struct mrsas_evt_log_info *)dcmd_dma_obj.buffer; eli->newest_seq_num = ddi_get32(cmd->frame_dma_obj.acc_handle, &eli_tmp->newest_seq_num); ret = DDI_SUCCESS; } if (mrsas_free_dma_obj(instance, dcmd_dma_obj) != DDI_SUCCESS) ret = DDI_FAILURE; if (instance->tbolt) { return_raid_msg_mfi_pkt(instance, cmd); } else { mrsas_return_mfi_pkt(instance, cmd); } return (ret); } /* * start_mfi_aen */ static int start_mfi_aen(struct mrsas_instance *instance) { int ret = 0; struct mrsas_evt_log_info eli; union mrsas_evt_class_locale class_locale; /* get the latest sequence number from FW */ (void) memset(&eli, 0, sizeof (struct mrsas_evt_log_info)); if (get_seq_num(instance, &eli)) { dev_err(instance->dip, CE_WARN, "start_mfi_aen: failed to get seq num"); return (-1); } /* register AEN with FW for latest sequence number plus 1 */ class_locale.members.reserved = 0; class_locale.members.locale = LE_16(MR_EVT_LOCALE_ALL); class_locale.members.class = MR_EVT_CLASS_INFO; class_locale.word = LE_32(class_locale.word); ret = register_mfi_aen(instance, eli.newest_seq_num + 1, class_locale.word); if (ret) { dev_err(instance->dip, CE_WARN, "start_mfi_aen: aen registration failed"); return (-1); } return (ret); } /* * flush_cache */ static void flush_cache(struct mrsas_instance *instance) { struct mrsas_cmd *cmd = NULL; struct mrsas_dcmd_frame *dcmd; if (instance->tbolt) { cmd = get_raid_msg_mfi_pkt(instance); } else { cmd = mrsas_get_mfi_pkt(instance); } if (!cmd) { con_log(CL_ANN1, (CE_WARN, "flush_cache():Failed to get a cmd for flush_cache")); DTRACE_PROBE2(flush_cache_err, uint16_t, instance->fw_outstanding, uint16_t, instance->max_fw_cmds); return; } /* Clear the frame buffer and assign back the context id */ (void) memset((char *)&cmd->frame[0], 0, sizeof (union mrsas_frame)); ddi_put32(cmd->frame_dma_obj.acc_handle, &cmd->frame->hdr.context, cmd->index); dcmd = &cmd->frame->dcmd; (void) memset(dcmd->mbox.b, 0, DCMD_MBOX_SZ); ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->cmd, MFI_CMD_OP_DCMD); ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->cmd_status, 0x0); ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->sge_count, 0); ddi_put16(cmd->frame_dma_obj.acc_handle, &dcmd->flags, MFI_FRAME_DIR_NONE); ddi_put16(cmd->frame_dma_obj.acc_handle, &dcmd->timeout, 0); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->data_xfer_len, 0); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->opcode, MR_DCMD_CTRL_CACHE_FLUSH); ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->mbox.b[0], MR_FLUSH_CTRL_CACHE | MR_FLUSH_DISK_CACHE); cmd->frame_count = 1; if (instance->tbolt) { mr_sas_tbolt_build_mfi_cmd(instance, cmd); } if (instance->func_ptr->issue_cmd_in_poll_mode(instance, cmd)) { con_log(CL_ANN1, (CE_WARN, "flush_cache: failed to issue MFI_DCMD_CTRL_CACHE_FLUSH")); } con_log(CL_ANN1, (CE_CONT, "flush_cache done")); if (instance->tbolt) { return_raid_msg_mfi_pkt(instance, cmd); } else { mrsas_return_mfi_pkt(instance, cmd); } } /* * service_mfi_aen- Completes an AEN command * @instance: Adapter soft state * @cmd: Command to be completed * */ void service_mfi_aen(struct mrsas_instance *instance, struct mrsas_cmd *cmd) { uint32_t seq_num; struct mrsas_evt_detail *evt_detail = (struct mrsas_evt_detail *)instance->mfi_evt_detail_obj.buffer; int rval = 0; int tgt = 0; uint8_t dtype; mrsas_pd_address_t *pd_addr; ddi_acc_handle_t acc_handle; con_log(CL_ANN, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); acc_handle = cmd->frame_dma_obj.acc_handle; cmd->cmd_status = ddi_get8(acc_handle, &cmd->frame->io.cmd_status); if (cmd->cmd_status == ENODATA) { cmd->cmd_status = 0; } /* * log the MFI AEN event to the sysevent queue so that * application will get noticed */ if (ddi_log_sysevent(instance->dip, DDI_VENDOR_LSI, "LSIMEGA", "SAS", NULL, NULL, DDI_NOSLEEP) != DDI_SUCCESS) { int instance_no = ddi_get_instance(instance->dip); con_log(CL_ANN, (CE_WARN, "mr_sas%d: Failed to log AEN event", instance_no)); } /* * Check for any ld devices that has changed state. i.e. online * or offline. */ con_log(CL_ANN1, (CE_CONT, "AEN: code = %x class = %x locale = %x args = %x", ddi_get32(acc_handle, &evt_detail->code), evt_detail->cl.members.class, ddi_get16(acc_handle, &evt_detail->cl.members.locale), ddi_get8(acc_handle, &evt_detail->arg_type))); switch (ddi_get32(acc_handle, &evt_detail->code)) { case MR_EVT_CFG_CLEARED: { for (tgt = 0; tgt < MRDRV_MAX_LD; tgt++) { if (instance->mr_ld_list[tgt].dip != NULL) { mutex_enter(&instance->config_dev_mtx); instance->mr_ld_list[tgt].flag = (uint8_t)~MRDRV_TGT_VALID; mutex_exit(&instance->config_dev_mtx); rval = mrsas_service_evt(instance, tgt, 0, MRSAS_EVT_UNCONFIG_TGT, 0); con_log(CL_ANN1, (CE_WARN, "mr_sas: CFG CLEARED AEN rval = %d " "tgt id = %d", rval, tgt)); } } break; } case MR_EVT_LD_DELETED: { tgt = ddi_get16(acc_handle, &evt_detail->args.ld.target_id); mutex_enter(&instance->config_dev_mtx); instance->mr_ld_list[tgt].flag = (uint8_t)~MRDRV_TGT_VALID; mutex_exit(&instance->config_dev_mtx); rval = mrsas_service_evt(instance, ddi_get16(acc_handle, &evt_detail->args.ld.target_id), 0, MRSAS_EVT_UNCONFIG_TGT, 0); con_log(CL_ANN1, (CE_WARN, "mr_sas: LD DELETED AEN rval = %d " "tgt id = %d index = %d", rval, ddi_get16(acc_handle, &evt_detail->args.ld.target_id), ddi_get8(acc_handle, &evt_detail->args.ld.ld_index))); break; } /* End of MR_EVT_LD_DELETED */ case MR_EVT_LD_CREATED: { rval = mrsas_service_evt(instance, ddi_get16(acc_handle, &evt_detail->args.ld.target_id), 0, MRSAS_EVT_CONFIG_TGT, 0); con_log(CL_ANN1, (CE_WARN, "mr_sas: LD CREATED AEN rval = %d " "tgt id = %d index = %d", rval, ddi_get16(acc_handle, &evt_detail->args.ld.target_id), ddi_get8(acc_handle, &evt_detail->args.ld.ld_index))); break; } /* End of MR_EVT_LD_CREATED */ case MR_EVT_PD_REMOVED_EXT: { if (instance->tbolt || instance->skinny) { pd_addr = &evt_detail->args.pd_addr; dtype = pd_addr->scsi_dev_type; con_log(CL_DLEVEL1, (CE_NOTE, " MR_EVT_PD_REMOVED_EXT: dtype = %x," " arg_type = %d ", dtype, evt_detail->arg_type)); tgt = ddi_get16(acc_handle, &evt_detail->args.pd.device_id); mutex_enter(&instance->config_dev_mtx); instance->mr_tbolt_pd_list[tgt].flag = (uint8_t)~MRDRV_TGT_VALID; mutex_exit(&instance->config_dev_mtx); rval = mrsas_service_evt(instance, ddi_get16( acc_handle, &evt_detail->args.pd.device_id), 1, MRSAS_EVT_UNCONFIG_TGT, 0); con_log(CL_ANN1, (CE_WARN, "mr_sas: PD_REMOVED:" "rval = %d tgt id = %d ", rval, ddi_get16(acc_handle, &evt_detail->args.pd.device_id))); } break; } /* End of MR_EVT_PD_REMOVED_EXT */ case MR_EVT_PD_INSERTED_EXT: { if (instance->tbolt || instance->skinny) { rval = mrsas_service_evt(instance, ddi_get16(acc_handle, &evt_detail->args.pd.device_id), 1, MRSAS_EVT_CONFIG_TGT, 0); con_log(CL_ANN1, (CE_WARN, "mr_sas: PD_INSERTEDi_EXT:" "rval = %d tgt id = %d ", rval, ddi_get16(acc_handle, &evt_detail->args.pd.device_id))); } break; } /* End of MR_EVT_PD_INSERTED_EXT */ case MR_EVT_PD_STATE_CHANGE: { if (instance->tbolt || instance->skinny) { tgt = ddi_get16(acc_handle, &evt_detail->args.pd.device_id); if ((evt_detail->args.pd_state.prevState == PD_SYSTEM) && (evt_detail->args.pd_state.newState != PD_SYSTEM)) { mutex_enter(&instance->config_dev_mtx); instance->mr_tbolt_pd_list[tgt].flag = (uint8_t)~MRDRV_TGT_VALID; mutex_exit(&instance->config_dev_mtx); rval = mrsas_service_evt(instance, ddi_get16(acc_handle, &evt_detail->args.pd.device_id), 1, MRSAS_EVT_UNCONFIG_TGT, 0); con_log(CL_ANN1, (CE_WARN, "mr_sas: PD_REMOVED:" "rval = %d tgt id = %d ", rval, ddi_get16(acc_handle, &evt_detail->args.pd.device_id))); break; } if ((evt_detail->args.pd_state.prevState == UNCONFIGURED_GOOD) && (evt_detail->args.pd_state.newState == PD_SYSTEM)) { rval = mrsas_service_evt(instance, ddi_get16(acc_handle, &evt_detail->args.pd.device_id), 1, MRSAS_EVT_CONFIG_TGT, 0); con_log(CL_ANN1, (CE_WARN, "mr_sas: PD_INSERTED: rval = %d " " tgt id = %d ", rval, ddi_get16(acc_handle, &evt_detail->args.pd.device_id))); break; } } break; } } /* End of Main Switch */ /* get copy of seq_num and class/locale for re-registration */ seq_num = ddi_get32(acc_handle, &evt_detail->seq_num); seq_num++; (void) memset(instance->mfi_evt_detail_obj.buffer, 0, sizeof (struct mrsas_evt_detail)); ddi_put8(acc_handle, &cmd->frame->dcmd.cmd_status, 0x0); ddi_put32(acc_handle, &cmd->frame->dcmd.mbox.w[0], seq_num); instance->aen_seq_num = seq_num; cmd->frame_count = 1; cmd->retry_count_for_ocr = 0; cmd->drv_pkt_time = 0; /* Issue the aen registration frame */ instance->func_ptr->issue_cmd(cmd, instance); } /* * complete_cmd_in_sync_mode - Completes an internal command * @instance: Adapter soft state * @cmd: Command to be completed * * The issue_cmd_in_sync_mode() function waits for a command to complete * after it issues a command. This function wakes up that waiting routine by * calling wake_up() on the wait queue. */ static void complete_cmd_in_sync_mode(struct mrsas_instance *instance, struct mrsas_cmd *cmd) { cmd->cmd_status = ddi_get8(cmd->frame_dma_obj.acc_handle, &cmd->frame->io.cmd_status); cmd->sync_cmd = MRSAS_FALSE; con_log(CL_ANN1, (CE_NOTE, "complete_cmd_in_sync_mode called %p \n", (void *)cmd)); mutex_enter(&instance->int_cmd_mtx); if (cmd->cmd_status == ENODATA) { cmd->cmd_status = 0; } cv_broadcast(&instance->int_cmd_cv); mutex_exit(&instance->int_cmd_mtx); } /* * Call this function inside mrsas_softintr. * mrsas_initiate_ocr_if_fw_is_faulty - Initiates OCR if FW status is faulty * @instance: Adapter soft state */ static uint32_t mrsas_initiate_ocr_if_fw_is_faulty(struct mrsas_instance *instance) { uint32_t cur_abs_reg_val; uint32_t fw_state; cur_abs_reg_val = instance->func_ptr->read_fw_status_reg(instance); fw_state = cur_abs_reg_val & MFI_STATE_MASK; if (fw_state == MFI_STATE_FAULT) { if (instance->disable_online_ctrl_reset == 1) { dev_err(instance->dip, CE_WARN, "mrsas_initiate_ocr_if_fw_is_faulty: " "FW in Fault state, detected in ISR: " "FW doesn't support ocr "); return (ADAPTER_RESET_NOT_REQUIRED); } else { con_log(CL_ANN, (CE_NOTE, "mrsas_initiate_ocr_if_fw_is_faulty: FW in Fault " "state, detected in ISR: FW supports ocr ")); return (ADAPTER_RESET_REQUIRED); } } return (ADAPTER_RESET_NOT_REQUIRED); } /* * mrsas_softintr - The Software ISR * @param arg : HBA soft state * * called from high-level interrupt if hi-level interrupt are not there, * otherwise triggered as a soft interrupt */ static uint_t mrsas_softintr(struct mrsas_instance *instance) { struct scsi_pkt *pkt; struct scsa_cmd *acmd; struct mrsas_cmd *cmd; struct mlist_head *pos, *next; mlist_t process_list; struct mrsas_header *hdr; struct scsi_arq_status *arqstat; con_log(CL_ANN1, (CE_NOTE, "mrsas_softintr() called.")); ASSERT(instance); mutex_enter(&instance->completed_pool_mtx); if (mlist_empty(&instance->completed_pool_list)) { mutex_exit(&instance->completed_pool_mtx); return (DDI_INTR_CLAIMED); } instance->softint_running = 1; INIT_LIST_HEAD(&process_list); mlist_splice(&instance->completed_pool_list, &process_list); INIT_LIST_HEAD(&instance->completed_pool_list); mutex_exit(&instance->completed_pool_mtx); /* perform all callbacks first, before releasing the SCBs */ mlist_for_each_safe(pos, next, &process_list) { cmd = mlist_entry(pos, struct mrsas_cmd, list); /* syncronize the Cmd frame for the controller */ (void) ddi_dma_sync(cmd->frame_dma_obj.dma_handle, 0, 0, DDI_DMA_SYNC_FORCPU); if (mrsas_check_dma_handle(cmd->frame_dma_obj.dma_handle) != DDI_SUCCESS) { mrsas_fm_ereport(instance, DDI_FM_DEVICE_NO_RESPONSE); ddi_fm_service_impact(instance->dip, DDI_SERVICE_LOST); con_log(CL_ANN1, (CE_WARN, "mrsas_softintr: " "FMA check reports DMA handle failure")); return (DDI_INTR_CLAIMED); } hdr = &cmd->frame->hdr; /* remove the internal command from the process list */ mlist_del_init(&cmd->list); switch (ddi_get8(cmd->frame_dma_obj.acc_handle, &hdr->cmd)) { case MFI_CMD_OP_PD_SCSI: case MFI_CMD_OP_LD_SCSI: case MFI_CMD_OP_LD_READ: case MFI_CMD_OP_LD_WRITE: /* * MFI_CMD_OP_PD_SCSI and MFI_CMD_OP_LD_SCSI * could have been issued either through an * IO path or an IOCTL path. If it was via IOCTL, * we will send it to internal completion. */ if (cmd->sync_cmd == MRSAS_TRUE) { complete_cmd_in_sync_mode(instance, cmd); break; } /* regular commands */ acmd = cmd->cmd; pkt = CMD2PKT(acmd); if (acmd->cmd_flags & CFLAG_DMAVALID) { if (acmd->cmd_flags & CFLAG_CONSISTENT) { (void) ddi_dma_sync(acmd->cmd_dmahandle, acmd->cmd_dma_offset, acmd->cmd_dma_len, DDI_DMA_SYNC_FORCPU); } } pkt->pkt_reason = CMD_CMPLT; pkt->pkt_statistics = 0; pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD | STATE_XFERRED_DATA | STATE_GOT_STATUS; con_log(CL_ANN, (CE_CONT, "CDB[0] = %x completed for %s: size %lx context %x", pkt->pkt_cdbp[0], ((acmd->islogical) ? "LD" : "PD"), acmd->cmd_dmacount, hdr->context)); DTRACE_PROBE3(softintr_cdb, uint8_t, pkt->pkt_cdbp[0], uint_t, acmd->cmd_cdblen, ulong_t, acmd->cmd_dmacount); if (pkt->pkt_cdbp[0] == SCMD_INQUIRY) { struct scsi_inquiry *inq; if (acmd->cmd_dmacount != 0) { bp_mapin(acmd->cmd_buf); inq = (struct scsi_inquiry *) acmd->cmd_buf->b_un.b_addr; if (hdr->cmd_status == MFI_STAT_OK) { display_scsi_inquiry( (caddr_t)inq); } } } DTRACE_PROBE2(softintr_done, uint8_t, hdr->cmd, uint8_t, hdr->cmd_status); switch (hdr->cmd_status) { case MFI_STAT_OK: pkt->pkt_scbp[0] = STATUS_GOOD; break; case MFI_STAT_LD_CC_IN_PROGRESS: case MFI_STAT_LD_RECON_IN_PROGRESS: pkt->pkt_scbp[0] = STATUS_GOOD; break; case MFI_STAT_LD_INIT_IN_PROGRESS: con_log(CL_ANN, (CE_WARN, "Initialization in Progress")); pkt->pkt_reason = CMD_TRAN_ERR; break; case MFI_STAT_SCSI_DONE_WITH_ERROR: con_log(CL_ANN, (CE_CONT, "scsi_done error")); pkt->pkt_reason = CMD_CMPLT; ((struct scsi_status *) pkt->pkt_scbp)->sts_chk = 1; if (pkt->pkt_cdbp[0] == SCMD_TEST_UNIT_READY) { con_log(CL_ANN, (CE_WARN, "TEST_UNIT_READY fail")); } else { pkt->pkt_state |= STATE_ARQ_DONE; arqstat = (void *)(pkt->pkt_scbp); arqstat->sts_rqpkt_reason = CMD_CMPLT; arqstat->sts_rqpkt_resid = 0; arqstat->sts_rqpkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD | STATE_XFERRED_DATA; *(uint8_t *)&arqstat->sts_rqpkt_status = STATUS_GOOD; ddi_rep_get8( cmd->frame_dma_obj.acc_handle, (uint8_t *) &(arqstat->sts_sensedata), cmd->sense, sizeof (struct scsi_extended_sense), DDI_DEV_AUTOINCR); } break; case MFI_STAT_LD_OFFLINE: case MFI_STAT_DEVICE_NOT_FOUND: con_log(CL_ANN, (CE_CONT, "mrsas_softintr:device not found error")); pkt->pkt_reason = CMD_DEV_GONE; pkt->pkt_statistics = STAT_DISCON; break; case MFI_STAT_LD_LBA_OUT_OF_RANGE: pkt->pkt_state |= STATE_ARQ_DONE; pkt->pkt_reason = CMD_CMPLT; ((struct scsi_status *) pkt->pkt_scbp)->sts_chk = 1; arqstat = (void *)(pkt->pkt_scbp); arqstat->sts_rqpkt_reason = CMD_CMPLT; arqstat->sts_rqpkt_resid = 0; arqstat->sts_rqpkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD | STATE_XFERRED_DATA; *(uint8_t *)&arqstat->sts_rqpkt_status = STATUS_GOOD; arqstat->sts_sensedata.es_valid = 1; arqstat->sts_sensedata.es_key = KEY_ILLEGAL_REQUEST; arqstat->sts_sensedata.es_class = CLASS_EXTENDED_SENSE; /* * LOGICAL BLOCK ADDRESS OUT OF RANGE: * ASC: 0x21h; ASCQ: 0x00h; */ arqstat->sts_sensedata.es_add_code = 0x21; arqstat->sts_sensedata.es_qual_code = 0x00; break; default: con_log(CL_ANN, (CE_CONT, "Unknown status!")); pkt->pkt_reason = CMD_TRAN_ERR; break; } atomic_add_16(&instance->fw_outstanding, (-1)); (void) mrsas_common_check(instance, cmd); if (acmd->cmd_dmahandle) { if (mrsas_check_dma_handle( acmd->cmd_dmahandle) != DDI_SUCCESS) { ddi_fm_service_impact(instance->dip, DDI_SERVICE_UNAFFECTED); pkt->pkt_reason = CMD_TRAN_ERR; pkt->pkt_statistics = 0; } } mrsas_return_mfi_pkt(instance, cmd); /* Call the callback routine */ if (((pkt->pkt_flags & FLAG_NOINTR) == 0) && pkt->pkt_comp) { (*pkt->pkt_comp)(pkt); } break; case MFI_CMD_OP_SMP: case MFI_CMD_OP_STP: complete_cmd_in_sync_mode(instance, cmd); break; case MFI_CMD_OP_DCMD: /* see if got an event notification */ if (ddi_get32(cmd->frame_dma_obj.acc_handle, &cmd->frame->dcmd.opcode) == MR_DCMD_CTRL_EVENT_WAIT) { if ((instance->aen_cmd == cmd) && (instance->aen_cmd->abort_aen)) { con_log(CL_ANN, (CE_WARN, "mrsas_softintr: " "aborted_aen returned")); } else { atomic_add_16(&instance->fw_outstanding, (-1)); service_mfi_aen(instance, cmd); } } else { complete_cmd_in_sync_mode(instance, cmd); } break; case MFI_CMD_OP_ABORT: con_log(CL_ANN, (CE_NOTE, "MFI_CMD_OP_ABORT complete")); /* * MFI_CMD_OP_ABORT successfully completed * in the synchronous mode */ complete_cmd_in_sync_mode(instance, cmd); break; default: mrsas_fm_ereport(instance, DDI_FM_DEVICE_NO_RESPONSE); ddi_fm_service_impact(instance->dip, DDI_SERVICE_LOST); if (cmd->pkt != NULL) { pkt = cmd->pkt; if (((pkt->pkt_flags & FLAG_NOINTR) == 0) && pkt->pkt_comp) { con_log(CL_ANN1, (CE_CONT, "posting to " "scsa cmd %p index %x pkt %p" "time %llx, default ", (void *)cmd, cmd->index, (void *)pkt, gethrtime())); (*pkt->pkt_comp)(pkt); } } con_log(CL_ANN, (CE_WARN, "Cmd type unknown !")); break; } } instance->softint_running = 0; return (DDI_INTR_CLAIMED); } /* * mrsas_alloc_dma_obj * * Allocate the memory and other resources for an dma object. */ int mrsas_alloc_dma_obj(struct mrsas_instance *instance, dma_obj_t *obj, uchar_t endian_flags) { int i; size_t alen = 0; uint_t cookie_cnt; struct ddi_device_acc_attr tmp_endian_attr; tmp_endian_attr = endian_attr; tmp_endian_attr.devacc_attr_endian_flags = endian_flags; tmp_endian_attr.devacc_attr_access = DDI_DEFAULT_ACC; i = ddi_dma_alloc_handle(instance->dip, &obj->dma_attr, DDI_DMA_SLEEP, NULL, &obj->dma_handle); if (i != DDI_SUCCESS) { switch (i) { case DDI_DMA_BADATTR : con_log(CL_ANN, (CE_WARN, "Failed ddi_dma_alloc_handle- Bad attribute")); break; case DDI_DMA_NORESOURCES : con_log(CL_ANN, (CE_WARN, "Failed ddi_dma_alloc_handle- No Resources")); break; default : con_log(CL_ANN, (CE_WARN, "Failed ddi_dma_alloc_handle: " "unknown status %d", i)); break; } return (-1); } if ((ddi_dma_mem_alloc(obj->dma_handle, obj->size, &tmp_endian_attr, DDI_DMA_RDWR | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &obj->buffer, &alen, &obj->acc_handle) != DDI_SUCCESS) || alen < obj->size) { ddi_dma_free_handle(&obj->dma_handle); con_log(CL_ANN, (CE_WARN, "Failed : ddi_dma_mem_alloc")); return (-1); } if (ddi_dma_addr_bind_handle(obj->dma_handle, NULL, obj->buffer, obj->size, DDI_DMA_RDWR | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &obj->dma_cookie[0], &cookie_cnt) != DDI_SUCCESS) { ddi_dma_mem_free(&obj->acc_handle); ddi_dma_free_handle(&obj->dma_handle); con_log(CL_ANN, (CE_WARN, "Failed : ddi_dma_addr_bind_handle")); return (-1); } if (mrsas_check_dma_handle(obj->dma_handle) != DDI_SUCCESS) { ddi_fm_service_impact(instance->dip, DDI_SERVICE_LOST); return (-1); } if (mrsas_check_acc_handle(obj->acc_handle) != DDI_SUCCESS) { ddi_fm_service_impact(instance->dip, DDI_SERVICE_LOST); return (-1); } return (cookie_cnt); } /* * mrsas_free_dma_obj(struct mrsas_instance *, dma_obj_t) * * De-allocate the memory and other resources for an dma object, which must * have been alloated by a previous call to mrsas_alloc_dma_obj() */ int mrsas_free_dma_obj(struct mrsas_instance *instance, dma_obj_t obj) { if ((obj.dma_handle == NULL) || (obj.acc_handle == NULL)) { return (DDI_SUCCESS); } /* * NOTE: These check-handle functions fail if *_handle == NULL, but * this function succeeds because of the previous check. */ if (mrsas_check_dma_handle(obj.dma_handle) != DDI_SUCCESS) { ddi_fm_service_impact(instance->dip, DDI_SERVICE_UNAFFECTED); return (DDI_FAILURE); } if (mrsas_check_acc_handle(obj.acc_handle) != DDI_SUCCESS) { ddi_fm_service_impact(instance->dip, DDI_SERVICE_UNAFFECTED); return (DDI_FAILURE); } (void) ddi_dma_unbind_handle(obj.dma_handle); ddi_dma_mem_free(&obj.acc_handle); ddi_dma_free_handle(&obj.dma_handle); obj.acc_handle = NULL; return (DDI_SUCCESS); } /* * mrsas_dma_alloc(instance_t *, struct scsi_pkt *, struct buf *, * int, int (*)()) * * Allocate dma resources for a new scsi command */ int mrsas_dma_alloc(struct mrsas_instance *instance, struct scsi_pkt *pkt, struct buf *bp, int flags, int (*callback)()) { int dma_flags; int (*cb)(caddr_t); int i; ddi_dma_attr_t tmp_dma_attr = mrsas_generic_dma_attr; struct scsa_cmd *acmd = PKT2CMD(pkt); acmd->cmd_buf = bp; if (bp->b_flags & B_READ) { acmd->cmd_flags &= ~CFLAG_DMASEND; dma_flags = DDI_DMA_READ; } else { acmd->cmd_flags |= CFLAG_DMASEND; dma_flags = DDI_DMA_WRITE; } if (flags & PKT_CONSISTENT) { acmd->cmd_flags |= CFLAG_CONSISTENT; dma_flags |= DDI_DMA_CONSISTENT; } if (flags & PKT_DMA_PARTIAL) { dma_flags |= DDI_DMA_PARTIAL; } dma_flags |= DDI_DMA_REDZONE; cb = (callback == NULL_FUNC) ? DDI_DMA_DONTWAIT : DDI_DMA_SLEEP; tmp_dma_attr.dma_attr_sgllen = instance->max_num_sge; tmp_dma_attr.dma_attr_addr_hi = 0xffffffffffffffffull; if (instance->tbolt) { /* OCR-RESET FIX */ tmp_dma_attr.dma_attr_count_max = (U64)mrsas_tbolt_max_cap_maxxfer; /* limit to 256K */ tmp_dma_attr.dma_attr_maxxfer = (U64)mrsas_tbolt_max_cap_maxxfer; /* limit to 256K */ } if ((i = ddi_dma_alloc_handle(instance->dip, &tmp_dma_attr, cb, 0, &acmd->cmd_dmahandle)) != DDI_SUCCESS) { switch (i) { case DDI_DMA_BADATTR: bioerror(bp, EFAULT); return (DDI_FAILURE); case DDI_DMA_NORESOURCES: bioerror(bp, 0); return (DDI_FAILURE); default: con_log(CL_ANN, (CE_PANIC, "ddi_dma_alloc_handle: " "impossible result (0x%x)", i)); bioerror(bp, EFAULT); return (DDI_FAILURE); } } i = ddi_dma_buf_bind_handle(acmd->cmd_dmahandle, bp, dma_flags, cb, 0, &acmd->cmd_dmacookies[0], &acmd->cmd_ncookies); switch (i) { case DDI_DMA_PARTIAL_MAP: if ((dma_flags & DDI_DMA_PARTIAL) == 0) { con_log(CL_ANN, (CE_PANIC, "ddi_dma_buf_bind_handle: " "DDI_DMA_PARTIAL_MAP impossible")); goto no_dma_cookies; } if (ddi_dma_numwin(acmd->cmd_dmahandle, &acmd->cmd_nwin) == DDI_FAILURE) { con_log(CL_ANN, (CE_PANIC, "ddi_dma_numwin failed")); goto no_dma_cookies; } if (ddi_dma_getwin(acmd->cmd_dmahandle, acmd->cmd_curwin, &acmd->cmd_dma_offset, &acmd->cmd_dma_len, &acmd->cmd_dmacookies[0], &acmd->cmd_ncookies) == DDI_FAILURE) { con_log(CL_ANN, (CE_PANIC, "ddi_dma_getwin failed")); goto no_dma_cookies; } goto get_dma_cookies; case DDI_DMA_MAPPED: acmd->cmd_nwin = 1; acmd->cmd_dma_len = 0; acmd->cmd_dma_offset = 0; get_dma_cookies: i = 0; acmd->cmd_dmacount = 0; for (;;) { acmd->cmd_dmacount += acmd->cmd_dmacookies[i++].dmac_size; if (i == instance->max_num_sge || i == acmd->cmd_ncookies) break; ddi_dma_nextcookie(acmd->cmd_dmahandle, &acmd->cmd_dmacookies[i]); } acmd->cmd_cookie = i; acmd->cmd_cookiecnt = i; acmd->cmd_flags |= CFLAG_DMAVALID; if (bp->b_bcount >= acmd->cmd_dmacount) { pkt->pkt_resid = bp->b_bcount - acmd->cmd_dmacount; } else { pkt->pkt_resid = 0; } return (DDI_SUCCESS); case DDI_DMA_NORESOURCES: bioerror(bp, 0); break; case DDI_DMA_NOMAPPING: bioerror(bp, EFAULT); break; case DDI_DMA_TOOBIG: bioerror(bp, EINVAL); break; case DDI_DMA_INUSE: con_log(CL_ANN, (CE_PANIC, "ddi_dma_buf_bind_handle:" " DDI_DMA_INUSE impossible")); break; default: con_log(CL_ANN, (CE_PANIC, "ddi_dma_buf_bind_handle: " "impossible result (0x%x)", i)); break; } no_dma_cookies: ddi_dma_free_handle(&acmd->cmd_dmahandle); acmd->cmd_dmahandle = NULL; acmd->cmd_flags &= ~CFLAG_DMAVALID; return (DDI_FAILURE); } /* * mrsas_dma_move(struct mrsas_instance *, struct scsi_pkt *, struct buf *) * * move dma resources to next dma window * */ int mrsas_dma_move(struct mrsas_instance *instance, struct scsi_pkt *pkt, struct buf *bp) { int i = 0; struct scsa_cmd *acmd = PKT2CMD(pkt); /* * If there are no more cookies remaining in this window, * must move to the next window first. */ if (acmd->cmd_cookie == acmd->cmd_ncookies) { if (acmd->cmd_curwin == acmd->cmd_nwin && acmd->cmd_nwin == 1) { return (DDI_SUCCESS); } /* at last window, cannot move */ if (++acmd->cmd_curwin >= acmd->cmd_nwin) { return (DDI_FAILURE); } if (ddi_dma_getwin(acmd->cmd_dmahandle, acmd->cmd_curwin, &acmd->cmd_dma_offset, &acmd->cmd_dma_len, &acmd->cmd_dmacookies[0], &acmd->cmd_ncookies) == DDI_FAILURE) { return (DDI_FAILURE); } acmd->cmd_cookie = 0; } else { /* still more cookies in this window - get the next one */ ddi_dma_nextcookie(acmd->cmd_dmahandle, &acmd->cmd_dmacookies[0]); } /* get remaining cookies in this window, up to our maximum */ for (;;) { acmd->cmd_dmacount += acmd->cmd_dmacookies[i++].dmac_size; acmd->cmd_cookie++; if (i == instance->max_num_sge || acmd->cmd_cookie == acmd->cmd_ncookies) { break; } ddi_dma_nextcookie(acmd->cmd_dmahandle, &acmd->cmd_dmacookies[i]); } acmd->cmd_cookiecnt = i; if (bp->b_bcount >= acmd->cmd_dmacount) { pkt->pkt_resid = bp->b_bcount - acmd->cmd_dmacount; } else { pkt->pkt_resid = 0; } return (DDI_SUCCESS); } /* * build_cmd */ static struct mrsas_cmd * build_cmd(struct mrsas_instance *instance, struct scsi_address *ap, struct scsi_pkt *pkt, uchar_t *cmd_done) { uint16_t flags = 0; uint32_t i; uint32_t sge_bytes; uint32_t tmp_data_xfer_len; ddi_acc_handle_t acc_handle; struct mrsas_cmd *cmd; struct mrsas_sge64 *mfi_sgl; struct mrsas_sge_ieee *mfi_sgl_ieee; struct scsa_cmd *acmd = PKT2CMD(pkt); struct mrsas_pthru_frame *pthru; struct mrsas_io_frame *ldio; /* find out if this is logical or physical drive command. */ acmd->islogical = MRDRV_IS_LOGICAL(ap); acmd->device_id = MAP_DEVICE_ID(instance, ap); *cmd_done = 0; /* get the command packet */ if (!(cmd = mrsas_get_mfi_pkt(instance))) { DTRACE_PROBE2(build_cmd_mfi_err, uint16_t, instance->fw_outstanding, uint16_t, instance->max_fw_cmds); return (NULL); } acc_handle = cmd->frame_dma_obj.acc_handle; /* Clear the frame buffer and assign back the context id */ (void) memset((char *)&cmd->frame[0], 0, sizeof (union mrsas_frame)); ddi_put32(acc_handle, &cmd->frame->hdr.context, cmd->index); cmd->pkt = pkt; cmd->cmd = acmd; DTRACE_PROBE3(build_cmds, uint8_t, pkt->pkt_cdbp[0], ulong_t, acmd->cmd_dmacount, ulong_t, acmd->cmd_dma_len); /* lets get the command directions */ if (acmd->cmd_flags & CFLAG_DMASEND) { flags = MFI_FRAME_DIR_WRITE; if (acmd->cmd_flags & CFLAG_CONSISTENT) { (void) ddi_dma_sync(acmd->cmd_dmahandle, acmd->cmd_dma_offset, acmd->cmd_dma_len, DDI_DMA_SYNC_FORDEV); } } else if (acmd->cmd_flags & ~CFLAG_DMASEND) { flags = MFI_FRAME_DIR_READ; if (acmd->cmd_flags & CFLAG_CONSISTENT) { (void) ddi_dma_sync(acmd->cmd_dmahandle, acmd->cmd_dma_offset, acmd->cmd_dma_len, DDI_DMA_SYNC_FORCPU); } } else { flags = MFI_FRAME_DIR_NONE; } if (instance->flag_ieee) { flags |= MFI_FRAME_IEEE; } flags |= MFI_FRAME_SGL64; switch (pkt->pkt_cdbp[0]) { /* * case SCMD_SYNCHRONIZE_CACHE: * flush_cache(instance); * mrsas_return_mfi_pkt(instance, cmd); * *cmd_done = 1; * * return (NULL); */ case SCMD_READ: case SCMD_WRITE: case SCMD_READ_G1: case SCMD_WRITE_G1: case SCMD_READ_G4: case SCMD_WRITE_G4: case SCMD_READ_G5: case SCMD_WRITE_G5: if (acmd->islogical) { ldio = (struct mrsas_io_frame *)cmd->frame; /* * preare the Logical IO frame: * 2nd bit is zero for all read cmds */ ddi_put8(acc_handle, &ldio->cmd, (pkt->pkt_cdbp[0] & 0x02) ? MFI_CMD_OP_LD_WRITE : MFI_CMD_OP_LD_READ); ddi_put8(acc_handle, &ldio->cmd_status, 0x0); ddi_put8(acc_handle, &ldio->scsi_status, 0x0); ddi_put8(acc_handle, &ldio->target_id, acmd->device_id); ddi_put16(acc_handle, &ldio->timeout, 0); ddi_put8(acc_handle, &ldio->reserved_0, 0); ddi_put16(acc_handle, &ldio->pad_0, 0); ddi_put16(acc_handle, &ldio->flags, flags); /* Initialize sense Information */ bzero(cmd->sense, SENSE_LENGTH); ddi_put8(acc_handle, &ldio->sense_len, SENSE_LENGTH); ddi_put32(acc_handle, &ldio->sense_buf_phys_addr_hi, 0); ddi_put32(acc_handle, &ldio->sense_buf_phys_addr_lo, cmd->sense_phys_addr); ddi_put32(acc_handle, &ldio->start_lba_hi, 0); ddi_put8(acc_handle, &ldio->access_byte, (acmd->cmd_cdblen != 6) ? pkt->pkt_cdbp[1] : 0); ddi_put8(acc_handle, &ldio->sge_count, acmd->cmd_cookiecnt); if (instance->flag_ieee) { mfi_sgl_ieee = (struct mrsas_sge_ieee *)&ldio->sgl; } else { mfi_sgl = (struct mrsas_sge64 *)&ldio->sgl; } (void) ddi_get32(acc_handle, &ldio->context); if (acmd->cmd_cdblen == CDB_GROUP0) { /* 6-byte cdb */ ddi_put32(acc_handle, &ldio->lba_count, ( (uint16_t)(pkt->pkt_cdbp[4]))); ddi_put32(acc_handle, &ldio->start_lba_lo, ( ((uint32_t)(pkt->pkt_cdbp[3])) | ((uint32_t)(pkt->pkt_cdbp[2]) << 8) | ((uint32_t)((pkt->pkt_cdbp[1]) & 0x1F) << 16))); } else if (acmd->cmd_cdblen == CDB_GROUP1) { /* 10-byte cdb */ ddi_put32(acc_handle, &ldio->lba_count, ( ((uint16_t)(pkt->pkt_cdbp[8])) | ((uint16_t)(pkt->pkt_cdbp[7]) << 8))); ddi_put32(acc_handle, &ldio->start_lba_lo, ( ((uint32_t)(pkt->pkt_cdbp[5])) | ((uint32_t)(pkt->pkt_cdbp[4]) << 8) | ((uint32_t)(pkt->pkt_cdbp[3]) << 16) | ((uint32_t)(pkt->pkt_cdbp[2]) << 24))); } else if (acmd->cmd_cdblen == CDB_GROUP5) { /* 12-byte cdb */ ddi_put32(acc_handle, &ldio->lba_count, ( ((uint32_t)(pkt->pkt_cdbp[9])) | ((uint32_t)(pkt->pkt_cdbp[8]) << 8) | ((uint32_t)(pkt->pkt_cdbp[7]) << 16) | ((uint32_t)(pkt->pkt_cdbp[6]) << 24))); ddi_put32(acc_handle, &ldio->start_lba_lo, ( ((uint32_t)(pkt->pkt_cdbp[5])) | ((uint32_t)(pkt->pkt_cdbp[4]) << 8) | ((uint32_t)(pkt->pkt_cdbp[3]) << 16) | ((uint32_t)(pkt->pkt_cdbp[2]) << 24))); } else if (acmd->cmd_cdblen == CDB_GROUP4) { /* 16-byte cdb */ ddi_put32(acc_handle, &ldio->lba_count, ( ((uint32_t)(pkt->pkt_cdbp[13])) | ((uint32_t)(pkt->pkt_cdbp[12]) << 8) | ((uint32_t)(pkt->pkt_cdbp[11]) << 16) | ((uint32_t)(pkt->pkt_cdbp[10]) << 24))); ddi_put32(acc_handle, &ldio->start_lba_lo, ( ((uint32_t)(pkt->pkt_cdbp[9])) | ((uint32_t)(pkt->pkt_cdbp[8]) << 8) | ((uint32_t)(pkt->pkt_cdbp[7]) << 16) | ((uint32_t)(pkt->pkt_cdbp[6]) << 24))); ddi_put32(acc_handle, &ldio->start_lba_hi, ( ((uint32_t)(pkt->pkt_cdbp[5])) | ((uint32_t)(pkt->pkt_cdbp[4]) << 8) | ((uint32_t)(pkt->pkt_cdbp[3]) << 16) | ((uint32_t)(pkt->pkt_cdbp[2]) << 24))); } break; } /* For all non-rd/wr and physical disk cmds */ /* FALLTHROUGH */ default: switch (pkt->pkt_cdbp[0]) { case SCMD_MODE_SENSE: case SCMD_MODE_SENSE_G1: { union scsi_cdb *cdbp; uint16_t page_code; cdbp = (void *)pkt->pkt_cdbp; page_code = (uint16_t)cdbp->cdb_un.sg.scsi[0]; switch (page_code) { case 0x3: case 0x4: (void) mrsas_mode_sense_build(pkt); mrsas_return_mfi_pkt(instance, cmd); *cmd_done = 1; return (NULL); } break; } default: break; } pthru = (struct mrsas_pthru_frame *)cmd->frame; /* prepare the DCDB frame */ ddi_put8(acc_handle, &pthru->cmd, (acmd->islogical) ? MFI_CMD_OP_LD_SCSI : MFI_CMD_OP_PD_SCSI); ddi_put8(acc_handle, &pthru->cmd_status, 0x0); ddi_put8(acc_handle, &pthru->scsi_status, 0x0); ddi_put8(acc_handle, &pthru->target_id, acmd->device_id); ddi_put8(acc_handle, &pthru->lun, 0); ddi_put8(acc_handle, &pthru->cdb_len, acmd->cmd_cdblen); ddi_put16(acc_handle, &pthru->timeout, 0); ddi_put16(acc_handle, &pthru->flags, flags); tmp_data_xfer_len = 0; for (i = 0; i < acmd->cmd_cookiecnt; i++) { tmp_data_xfer_len += acmd->cmd_dmacookies[i].dmac_size; } ddi_put32(acc_handle, &pthru->data_xfer_len, tmp_data_xfer_len); ddi_put8(acc_handle, &pthru->sge_count, acmd->cmd_cookiecnt); if (instance->flag_ieee) { mfi_sgl_ieee = (struct mrsas_sge_ieee *)&pthru->sgl; } else { mfi_sgl = (struct mrsas_sge64 *)&pthru->sgl; } bzero(cmd->sense, SENSE_LENGTH); ddi_put8(acc_handle, &pthru->sense_len, SENSE_LENGTH); ddi_put32(acc_handle, &pthru->sense_buf_phys_addr_hi, 0); ddi_put32(acc_handle, &pthru->sense_buf_phys_addr_lo, cmd->sense_phys_addr); (void) ddi_get32(acc_handle, &pthru->context); ddi_rep_put8(acc_handle, (uint8_t *)pkt->pkt_cdbp, (uint8_t *)pthru->cdb, acmd->cmd_cdblen, DDI_DEV_AUTOINCR); break; } /* prepare the scatter-gather list for the firmware */ if (instance->flag_ieee) { for (i = 0; i < acmd->cmd_cookiecnt; i++, mfi_sgl_ieee++) { ddi_put64(acc_handle, &mfi_sgl_ieee->phys_addr, acmd->cmd_dmacookies[i].dmac_laddress); ddi_put32(acc_handle, &mfi_sgl_ieee->length, acmd->cmd_dmacookies[i].dmac_size); } sge_bytes = sizeof (struct mrsas_sge_ieee)*acmd->cmd_cookiecnt; } else { for (i = 0; i < acmd->cmd_cookiecnt; i++, mfi_sgl++) { ddi_put64(acc_handle, &mfi_sgl->phys_addr, acmd->cmd_dmacookies[i].dmac_laddress); ddi_put32(acc_handle, &mfi_sgl->length, acmd->cmd_dmacookies[i].dmac_size); } sge_bytes = sizeof (struct mrsas_sge64)*acmd->cmd_cookiecnt; } cmd->frame_count = (sge_bytes / MRMFI_FRAME_SIZE) + ((sge_bytes % MRMFI_FRAME_SIZE) ? 1 : 0) + 1; if (cmd->frame_count >= 8) { cmd->frame_count = 8; } return (cmd); } /* * wait_for_outstanding - Wait for all outstanding cmds * @instance: Adapter soft state * * This function waits for upto MRDRV_RESET_WAIT_TIME seconds for FW to * complete all its outstanding commands. Returns error if one or more IOs * are pending after this time period. */ static int wait_for_outstanding(struct mrsas_instance *instance) { int i; uint32_t wait_time = 90; for (i = 0; i < wait_time; i++) { if (!instance->fw_outstanding) { break; } drv_usecwait(MILLISEC); /* wait for 1000 usecs */; } if (instance->fw_outstanding) { return (1); } return (0); } /* * issue_mfi_pthru */ static int issue_mfi_pthru(struct mrsas_instance *instance, struct mrsas_ioctl *ioctl, struct mrsas_cmd *cmd, int mode) { void *ubuf; uint32_t kphys_addr = 0; uint32_t xferlen = 0; uint32_t new_xfer_length = 0; uint_t model; ddi_acc_handle_t acc_handle = cmd->frame_dma_obj.acc_handle; dma_obj_t pthru_dma_obj; struct mrsas_pthru_frame *kpthru; struct mrsas_pthru_frame *pthru; int i; pthru = &cmd->frame->pthru; kpthru = (struct mrsas_pthru_frame *)&ioctl->frame[0]; if (instance->adapterresetinprogress) { con_log(CL_ANN1, (CE_WARN, "issue_mfi_pthru: Reset flag set, " "returning mfi_pkt and setting TRAN_BUSY\n")); return (DDI_FAILURE); } model = ddi_model_convert_from(mode & FMODELS); if (model == DDI_MODEL_ILP32) { con_log(CL_ANN1, (CE_CONT, "issue_mfi_pthru: DDI_MODEL_LP32")); xferlen = kpthru->sgl.sge32[0].length; ubuf = (void *)(ulong_t)kpthru->sgl.sge32[0].phys_addr; } else { #ifdef _ILP32 con_log(CL_ANN1, (CE_CONT, "issue_mfi_pthru: DDI_MODEL_LP32")); xferlen = kpthru->sgl.sge32[0].length; ubuf = (void *)(ulong_t)kpthru->sgl.sge32[0].phys_addr; #else con_log(CL_ANN1, (CE_CONT, "issue_mfi_pthru: DDI_MODEL_LP64")); xferlen = kpthru->sgl.sge64[0].length; ubuf = (void *)(ulong_t)kpthru->sgl.sge64[0].phys_addr; #endif } if (xferlen) { /* means IOCTL requires DMA */ /* allocate the data transfer buffer */ /* pthru_dma_obj.size = xferlen; */ MRSAS_GET_BOUNDARY_ALIGNED_LEN(xferlen, new_xfer_length, PAGESIZE); pthru_dma_obj.size = new_xfer_length; pthru_dma_obj.dma_attr = mrsas_generic_dma_attr; pthru_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU; pthru_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU; pthru_dma_obj.dma_attr.dma_attr_sgllen = 1; pthru_dma_obj.dma_attr.dma_attr_align = 1; /* allocate kernel buffer for DMA */ if (mrsas_alloc_dma_obj(instance, &pthru_dma_obj, (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) { con_log(CL_ANN, (CE_WARN, "issue_mfi_pthru: " "could not allocate data transfer buffer.")); return (DDI_FAILURE); } (void) memset(pthru_dma_obj.buffer, 0, xferlen); /* If IOCTL requires DMA WRITE, do ddi_copyin IOCTL data copy */ if (kpthru->flags & MFI_FRAME_DIR_WRITE) { for (i = 0; i < xferlen; i++) { if (ddi_copyin((uint8_t *)ubuf+i, (uint8_t *)pthru_dma_obj.buffer+i, 1, mode)) { con_log(CL_ANN, (CE_WARN, "issue_mfi_pthru : " "copy from user space failed")); return (DDI_FAILURE); } } } kphys_addr = pthru_dma_obj.dma_cookie[0].dmac_address; } ddi_put8(acc_handle, &pthru->cmd, kpthru->cmd); ddi_put8(acc_handle, &pthru->sense_len, SENSE_LENGTH); ddi_put8(acc_handle, &pthru->cmd_status, 0); ddi_put8(acc_handle, &pthru->scsi_status, 0); ddi_put8(acc_handle, &pthru->target_id, kpthru->target_id); ddi_put8(acc_handle, &pthru->lun, kpthru->lun); ddi_put8(acc_handle, &pthru->cdb_len, kpthru->cdb_len); ddi_put8(acc_handle, &pthru->sge_count, kpthru->sge_count); ddi_put16(acc_handle, &pthru->timeout, kpthru->timeout); ddi_put32(acc_handle, &pthru->data_xfer_len, kpthru->data_xfer_len); ddi_put32(acc_handle, &pthru->sense_buf_phys_addr_hi, 0); pthru->sense_buf_phys_addr_lo = cmd->sense_phys_addr; /* ddi_put32(acc_handle, &pthru->sense_buf_phys_addr_lo, 0); */ ddi_rep_put8(acc_handle, (uint8_t *)kpthru->cdb, (uint8_t *)pthru->cdb, pthru->cdb_len, DDI_DEV_AUTOINCR); ddi_put16(acc_handle, &pthru->flags, kpthru->flags & ~MFI_FRAME_SGL64); ddi_put32(acc_handle, &pthru->sgl.sge32[0].length, xferlen); ddi_put32(acc_handle, &pthru->sgl.sge32[0].phys_addr, kphys_addr); cmd->sync_cmd = MRSAS_TRUE; cmd->frame_count = 1; if (instance->tbolt) { mr_sas_tbolt_build_mfi_cmd(instance, cmd); } if (instance->func_ptr->issue_cmd_in_sync_mode(instance, cmd)) { con_log(CL_ANN, (CE_WARN, "issue_mfi_pthru: fw_ioctl failed")); } else { if (xferlen && kpthru->flags & MFI_FRAME_DIR_READ) { for (i = 0; i < xferlen; i++) { if (ddi_copyout( (uint8_t *)pthru_dma_obj.buffer+i, (uint8_t *)ubuf+i, 1, mode)) { con_log(CL_ANN, (CE_WARN, "issue_mfi_pthru : " "copy to user space failed")); return (DDI_FAILURE); } } } } kpthru->cmd_status = ddi_get8(acc_handle, &pthru->cmd_status); kpthru->scsi_status = ddi_get8(acc_handle, &pthru->scsi_status); con_log(CL_ANN, (CE_CONT, "issue_mfi_pthru: cmd_status %x, " "scsi_status %x", kpthru->cmd_status, kpthru->scsi_status)); DTRACE_PROBE3(issue_pthru, uint8_t, kpthru->cmd, uint8_t, kpthru->cmd_status, uint8_t, kpthru->scsi_status); if (kpthru->sense_len) { uint_t sense_len = SENSE_LENGTH; void *sense_ubuf = (void *)(ulong_t)kpthru->sense_buf_phys_addr_lo; if (kpthru->sense_len <= SENSE_LENGTH) { sense_len = kpthru->sense_len; } for (i = 0; i < sense_len; i++) { if (ddi_copyout( (uint8_t *)cmd->sense+i, (uint8_t *)sense_ubuf+i, 1, mode)) { con_log(CL_ANN, (CE_WARN, "issue_mfi_pthru : " "copy to user space failed")); } con_log(CL_DLEVEL1, (CE_WARN, "Copying Sense info sense_buff[%d] = 0x%X", i, *((uint8_t *)cmd->sense + i))); } } (void) ddi_dma_sync(cmd->frame_dma_obj.dma_handle, 0, 0, DDI_DMA_SYNC_FORDEV); if (xferlen) { /* free kernel buffer */ if (mrsas_free_dma_obj(instance, pthru_dma_obj) != DDI_SUCCESS) return (DDI_FAILURE); } return (DDI_SUCCESS); } /* * issue_mfi_dcmd */ static int issue_mfi_dcmd(struct mrsas_instance *instance, struct mrsas_ioctl *ioctl, struct mrsas_cmd *cmd, int mode) { void *ubuf; uint32_t kphys_addr = 0; uint32_t xferlen = 0; uint32_t new_xfer_length = 0; uint32_t model; dma_obj_t dcmd_dma_obj; struct mrsas_dcmd_frame *kdcmd; struct mrsas_dcmd_frame *dcmd; ddi_acc_handle_t acc_handle = cmd->frame_dma_obj.acc_handle; int i; dcmd = &cmd->frame->dcmd; kdcmd = (struct mrsas_dcmd_frame *)&ioctl->frame[0]; if (instance->adapterresetinprogress) { con_log(CL_ANN1, (CE_NOTE, "Reset flag set, " "returning mfi_pkt and setting TRAN_BUSY")); return (DDI_FAILURE); } model = ddi_model_convert_from(mode & FMODELS); if (model == DDI_MODEL_ILP32) { con_log(CL_ANN1, (CE_CONT, "issue_mfi_dcmd: DDI_MODEL_ILP32")); xferlen = kdcmd->sgl.sge32[0].length; ubuf = (void *)(ulong_t)kdcmd->sgl.sge32[0].phys_addr; } else { #ifdef _ILP32 con_log(CL_ANN1, (CE_CONT, "issue_mfi_dcmd: DDI_MODEL_ILP32")); xferlen = kdcmd->sgl.sge32[0].length; ubuf = (void *)(ulong_t)kdcmd->sgl.sge32[0].phys_addr; #else con_log(CL_ANN1, (CE_CONT, "issue_mfi_dcmd: DDI_MODEL_LP64")); xferlen = kdcmd->sgl.sge64[0].length; ubuf = (void *)(ulong_t)kdcmd->sgl.sge64[0].phys_addr; #endif } if (xferlen) { /* means IOCTL requires DMA */ /* allocate the data transfer buffer */ /* dcmd_dma_obj.size = xferlen; */ MRSAS_GET_BOUNDARY_ALIGNED_LEN(xferlen, new_xfer_length, PAGESIZE); dcmd_dma_obj.size = new_xfer_length; dcmd_dma_obj.dma_attr = mrsas_generic_dma_attr; dcmd_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU; dcmd_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU; dcmd_dma_obj.dma_attr.dma_attr_sgllen = 1; dcmd_dma_obj.dma_attr.dma_attr_align = 1; /* allocate kernel buffer for DMA */ if (mrsas_alloc_dma_obj(instance, &dcmd_dma_obj, (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) { con_log(CL_ANN, (CE_WARN, "issue_mfi_dcmd: could not " "allocate data transfer buffer.")); return (DDI_FAILURE); } (void) memset(dcmd_dma_obj.buffer, 0, xferlen); /* If IOCTL requires DMA WRITE, do ddi_copyin IOCTL data copy */ if (kdcmd->flags & MFI_FRAME_DIR_WRITE) { for (i = 0; i < xferlen; i++) { if (ddi_copyin((uint8_t *)ubuf + i, (uint8_t *)dcmd_dma_obj.buffer + i, 1, mode)) { con_log(CL_ANN, (CE_WARN, "issue_mfi_dcmd : " "copy from user space failed")); return (DDI_FAILURE); } } } kphys_addr = dcmd_dma_obj.dma_cookie[0].dmac_address; } ddi_put8(acc_handle, &dcmd->cmd, kdcmd->cmd); ddi_put8(acc_handle, &dcmd->cmd_status, 0); ddi_put8(acc_handle, &dcmd->sge_count, kdcmd->sge_count); ddi_put16(acc_handle, &dcmd->timeout, kdcmd->timeout); ddi_put32(acc_handle, &dcmd->data_xfer_len, kdcmd->data_xfer_len); ddi_put32(acc_handle, &dcmd->opcode, kdcmd->opcode); ddi_rep_put8(acc_handle, (uint8_t *)kdcmd->mbox.b, (uint8_t *)dcmd->mbox.b, DCMD_MBOX_SZ, DDI_DEV_AUTOINCR); ddi_put16(acc_handle, &dcmd->flags, kdcmd->flags & ~MFI_FRAME_SGL64); ddi_put32(acc_handle, &dcmd->sgl.sge32[0].length, xferlen); ddi_put32(acc_handle, &dcmd->sgl.sge32[0].phys_addr, kphys_addr); cmd->sync_cmd = MRSAS_TRUE; cmd->frame_count = 1; if (instance->tbolt) { mr_sas_tbolt_build_mfi_cmd(instance, cmd); } if (instance->func_ptr->issue_cmd_in_sync_mode(instance, cmd)) { con_log(CL_ANN, (CE_WARN, "issue_mfi_dcmd: fw_ioctl failed")); } else { if (xferlen && (kdcmd->flags & MFI_FRAME_DIR_READ)) { for (i = 0; i < xferlen; i++) { if (ddi_copyout( (uint8_t *)dcmd_dma_obj.buffer + i, (uint8_t *)ubuf + i, 1, mode)) { con_log(CL_ANN, (CE_WARN, "issue_mfi_dcmd : " "copy to user space failed")); return (DDI_FAILURE); } } } } kdcmd->cmd_status = ddi_get8(acc_handle, &dcmd->cmd_status); con_log(CL_ANN, (CE_CONT, "issue_mfi_dcmd: cmd_status %x", kdcmd->cmd_status)); DTRACE_PROBE3(issue_dcmd, uint32_t, kdcmd->opcode, uint8_t, kdcmd->cmd, uint8_t, kdcmd->cmd_status); if (xferlen) { /* free kernel buffer */ if (mrsas_free_dma_obj(instance, dcmd_dma_obj) != DDI_SUCCESS) return (DDI_FAILURE); } return (DDI_SUCCESS); } /* * issue_mfi_smp */ static int issue_mfi_smp(struct mrsas_instance *instance, struct mrsas_ioctl *ioctl, struct mrsas_cmd *cmd, int mode) { void *request_ubuf; void *response_ubuf; uint32_t request_xferlen = 0; uint32_t response_xferlen = 0; uint32_t new_xfer_length1 = 0; uint32_t new_xfer_length2 = 0; uint_t model; dma_obj_t request_dma_obj; dma_obj_t response_dma_obj; ddi_acc_handle_t acc_handle = cmd->frame_dma_obj.acc_handle; struct mrsas_smp_frame *ksmp; struct mrsas_smp_frame *smp; struct mrsas_sge32 *sge32; #ifndef _ILP32 struct mrsas_sge64 *sge64; #endif int i; uint64_t tmp_sas_addr; smp = &cmd->frame->smp; ksmp = (struct mrsas_smp_frame *)&ioctl->frame[0]; if (instance->adapterresetinprogress) { con_log(CL_ANN1, (CE_WARN, "Reset flag set, " "returning mfi_pkt and setting TRAN_BUSY\n")); return (DDI_FAILURE); } model = ddi_model_convert_from(mode & FMODELS); if (model == DDI_MODEL_ILP32) { con_log(CL_ANN1, (CE_CONT, "issue_mfi_smp: DDI_MODEL_ILP32")); sge32 = &ksmp->sgl[0].sge32[0]; response_xferlen = sge32[0].length; request_xferlen = sge32[1].length; con_log(CL_ANN, (CE_CONT, "issue_mfi_smp: " "response_xferlen = %x, request_xferlen = %x", response_xferlen, request_xferlen)); response_ubuf = (void *)(ulong_t)sge32[0].phys_addr; request_ubuf = (void *)(ulong_t)sge32[1].phys_addr; con_log(CL_ANN1, (CE_CONT, "issue_mfi_smp: " "response_ubuf = %p, request_ubuf = %p", response_ubuf, request_ubuf)); } else { #ifdef _ILP32 con_log(CL_ANN1, (CE_CONT, "issue_mfi_smp: DDI_MODEL_ILP32")); sge32 = &ksmp->sgl[0].sge32[0]; response_xferlen = sge32[0].length; request_xferlen = sge32[1].length; con_log(CL_ANN, (CE_CONT, "issue_mfi_smp: " "response_xferlen = %x, request_xferlen = %x", response_xferlen, request_xferlen)); response_ubuf = (void *)(ulong_t)sge32[0].phys_addr; request_ubuf = (void *)(ulong_t)sge32[1].phys_addr; con_log(CL_ANN1, (CE_CONT, "issue_mfi_smp: " "response_ubuf = %p, request_ubuf = %p", response_ubuf, request_ubuf)); #else con_log(CL_ANN1, (CE_CONT, "issue_mfi_smp: DDI_MODEL_LP64")); sge64 = &ksmp->sgl[0].sge64[0]; response_xferlen = sge64[0].length; request_xferlen = sge64[1].length; response_ubuf = (void *)(ulong_t)sge64[0].phys_addr; request_ubuf = (void *)(ulong_t)sge64[1].phys_addr; #endif } if (request_xferlen) { /* means IOCTL requires DMA */ /* allocate the data transfer buffer */ /* request_dma_obj.size = request_xferlen; */ MRSAS_GET_BOUNDARY_ALIGNED_LEN(request_xferlen, new_xfer_length1, PAGESIZE); request_dma_obj.size = new_xfer_length1; request_dma_obj.dma_attr = mrsas_generic_dma_attr; request_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU; request_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU; request_dma_obj.dma_attr.dma_attr_sgllen = 1; request_dma_obj.dma_attr.dma_attr_align = 1; /* allocate kernel buffer for DMA */ if (mrsas_alloc_dma_obj(instance, &request_dma_obj, (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) { con_log(CL_ANN, (CE_WARN, "issue_mfi_smp: " "could not allocate data transfer buffer.")); return (DDI_FAILURE); } (void) memset(request_dma_obj.buffer, 0, request_xferlen); /* If IOCTL requires DMA WRITE, do ddi_copyin IOCTL data copy */ for (i = 0; i < request_xferlen; i++) { if (ddi_copyin((uint8_t *)request_ubuf + i, (uint8_t *)request_dma_obj.buffer + i, 1, mode)) { con_log(CL_ANN, (CE_WARN, "issue_mfi_smp: " "copy from user space failed")); return (DDI_FAILURE); } } } if (response_xferlen) { /* means IOCTL requires DMA */ /* allocate the data transfer buffer */ /* response_dma_obj.size = response_xferlen; */ MRSAS_GET_BOUNDARY_ALIGNED_LEN(response_xferlen, new_xfer_length2, PAGESIZE); response_dma_obj.size = new_xfer_length2; response_dma_obj.dma_attr = mrsas_generic_dma_attr; response_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU; response_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU; response_dma_obj.dma_attr.dma_attr_sgllen = 1; response_dma_obj.dma_attr.dma_attr_align = 1; /* allocate kernel buffer for DMA */ if (mrsas_alloc_dma_obj(instance, &response_dma_obj, (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) { con_log(CL_ANN, (CE_WARN, "issue_mfi_smp: " "could not allocate data transfer buffer.")); return (DDI_FAILURE); } (void) memset(response_dma_obj.buffer, 0, response_xferlen); /* If IOCTL requires DMA WRITE, do ddi_copyin IOCTL data copy */ for (i = 0; i < response_xferlen; i++) { if (ddi_copyin((uint8_t *)response_ubuf + i, (uint8_t *)response_dma_obj.buffer + i, 1, mode)) { con_log(CL_ANN, (CE_WARN, "issue_mfi_smp: " "copy from user space failed")); return (DDI_FAILURE); } } } ddi_put8(acc_handle, &smp->cmd, ksmp->cmd); ddi_put8(acc_handle, &smp->cmd_status, 0); ddi_put8(acc_handle, &smp->connection_status, 0); ddi_put8(acc_handle, &smp->sge_count, ksmp->sge_count); /* smp->context = ksmp->context; */ ddi_put16(acc_handle, &smp->timeout, ksmp->timeout); ddi_put32(acc_handle, &smp->data_xfer_len, ksmp->data_xfer_len); bcopy((void *)&ksmp->sas_addr, (void *)&tmp_sas_addr, sizeof (uint64_t)); ddi_put64(acc_handle, &smp->sas_addr, tmp_sas_addr); ddi_put16(acc_handle, &smp->flags, ksmp->flags & ~MFI_FRAME_SGL64); model = ddi_model_convert_from(mode & FMODELS); if (model == DDI_MODEL_ILP32) { con_log(CL_ANN1, (CE_CONT, "issue_mfi_smp: DDI_MODEL_ILP32")); sge32 = &smp->sgl[0].sge32[0]; ddi_put32(acc_handle, &sge32[0].length, response_xferlen); ddi_put32(acc_handle, &sge32[0].phys_addr, response_dma_obj.dma_cookie[0].dmac_address); ddi_put32(acc_handle, &sge32[1].length, request_xferlen); ddi_put32(acc_handle, &sge32[1].phys_addr, request_dma_obj.dma_cookie[0].dmac_address); } else { #ifdef _ILP32 con_log(CL_ANN1, (CE_CONT, "issue_mfi_smp: DDI_MODEL_ILP32")); sge32 = &smp->sgl[0].sge32[0]; ddi_put32(acc_handle, &sge32[0].length, response_xferlen); ddi_put32(acc_handle, &sge32[0].phys_addr, response_dma_obj.dma_cookie[0].dmac_address); ddi_put32(acc_handle, &sge32[1].length, request_xferlen); ddi_put32(acc_handle, &sge32[1].phys_addr, request_dma_obj.dma_cookie[0].dmac_address); #else con_log(CL_ANN1, (CE_CONT, "issue_mfi_smp: DDI_MODEL_LP64")); sge64 = &smp->sgl[0].sge64[0]; ddi_put32(acc_handle, &sge64[0].length, response_xferlen); ddi_put64(acc_handle, &sge64[0].phys_addr, response_dma_obj.dma_cookie[0].dmac_address); ddi_put32(acc_handle, &sge64[1].length, request_xferlen); ddi_put64(acc_handle, &sge64[1].phys_addr, request_dma_obj.dma_cookie[0].dmac_address); #endif } con_log(CL_ANN1, (CE_CONT, "issue_mfi_smp : " "smp->response_xferlen = %d, smp->request_xferlen = %d " "smp->data_xfer_len = %d", ddi_get32(acc_handle, &sge32[0].length), ddi_get32(acc_handle, &sge32[1].length), ddi_get32(acc_handle, &smp->data_xfer_len))); cmd->sync_cmd = MRSAS_TRUE; cmd->frame_count = 1; if (instance->tbolt) { mr_sas_tbolt_build_mfi_cmd(instance, cmd); } if (instance->func_ptr->issue_cmd_in_sync_mode(instance, cmd)) { con_log(CL_ANN, (CE_WARN, "issue_mfi_smp: fw_ioctl failed")); } else { con_log(CL_ANN1, (CE_CONT, "issue_mfi_smp: copy to user space")); if (request_xferlen) { for (i = 0; i < request_xferlen; i++) { if (ddi_copyout( (uint8_t *)request_dma_obj.buffer + i, (uint8_t *)request_ubuf + i, 1, mode)) { con_log(CL_ANN, (CE_WARN, "issue_mfi_smp : copy to user space" " failed")); return (DDI_FAILURE); } } } if (response_xferlen) { for (i = 0; i < response_xferlen; i++) { if (ddi_copyout( (uint8_t *)response_dma_obj.buffer + i, (uint8_t *)response_ubuf + i, 1, mode)) { con_log(CL_ANN, (CE_WARN, "issue_mfi_smp : copy to " "user space failed")); return (DDI_FAILURE); } } } } ksmp->cmd_status = ddi_get8(acc_handle, &smp->cmd_status); con_log(CL_ANN1, (CE_NOTE, "issue_mfi_smp: smp->cmd_status = %d", ksmp->cmd_status)); DTRACE_PROBE2(issue_smp, uint8_t, ksmp->cmd, uint8_t, ksmp->cmd_status); if (request_xferlen) { /* free kernel buffer */ if (mrsas_free_dma_obj(instance, request_dma_obj) != DDI_SUCCESS) return (DDI_FAILURE); } if (response_xferlen) { /* free kernel buffer */ if (mrsas_free_dma_obj(instance, response_dma_obj) != DDI_SUCCESS) return (DDI_FAILURE); } return (DDI_SUCCESS); } /* * issue_mfi_stp */ static int issue_mfi_stp(struct mrsas_instance *instance, struct mrsas_ioctl *ioctl, struct mrsas_cmd *cmd, int mode) { void *fis_ubuf; void *data_ubuf; uint32_t fis_xferlen = 0; uint32_t new_xfer_length1 = 0; uint32_t new_xfer_length2 = 0; uint32_t data_xferlen = 0; uint_t model; dma_obj_t fis_dma_obj; dma_obj_t data_dma_obj; struct mrsas_stp_frame *kstp; struct mrsas_stp_frame *stp; ddi_acc_handle_t acc_handle = cmd->frame_dma_obj.acc_handle; int i; stp = &cmd->frame->stp; kstp = (struct mrsas_stp_frame *)&ioctl->frame[0]; if (instance->adapterresetinprogress) { con_log(CL_ANN1, (CE_WARN, "Reset flag set, " "returning mfi_pkt and setting TRAN_BUSY\n")); return (DDI_FAILURE); } model = ddi_model_convert_from(mode & FMODELS); if (model == DDI_MODEL_ILP32) { con_log(CL_ANN1, (CE_CONT, "issue_mfi_stp: DDI_MODEL_ILP32")); fis_xferlen = kstp->sgl.sge32[0].length; data_xferlen = kstp->sgl.sge32[1].length; fis_ubuf = (void *)(ulong_t)kstp->sgl.sge32[0].phys_addr; data_ubuf = (void *)(ulong_t)kstp->sgl.sge32[1].phys_addr; } else { #ifdef _ILP32 con_log(CL_ANN1, (CE_CONT, "issue_mfi_stp: DDI_MODEL_ILP32")); fis_xferlen = kstp->sgl.sge32[0].length; data_xferlen = kstp->sgl.sge32[1].length; fis_ubuf = (void *)(ulong_t)kstp->sgl.sge32[0].phys_addr; data_ubuf = (void *)(ulong_t)kstp->sgl.sge32[1].phys_addr; #else con_log(CL_ANN1, (CE_CONT, "issue_mfi_stp: DDI_MODEL_LP64")); fis_xferlen = kstp->sgl.sge64[0].length; data_xferlen = kstp->sgl.sge64[1].length; fis_ubuf = (void *)(ulong_t)kstp->sgl.sge64[0].phys_addr; data_ubuf = (void *)(ulong_t)kstp->sgl.sge64[1].phys_addr; #endif } if (fis_xferlen) { con_log(CL_ANN, (CE_CONT, "issue_mfi_stp: " "fis_ubuf = %p fis_xferlen = %x", fis_ubuf, fis_xferlen)); /* means IOCTL requires DMA */ /* allocate the data transfer buffer */ /* fis_dma_obj.size = fis_xferlen; */ MRSAS_GET_BOUNDARY_ALIGNED_LEN(fis_xferlen, new_xfer_length1, PAGESIZE); fis_dma_obj.size = new_xfer_length1; fis_dma_obj.dma_attr = mrsas_generic_dma_attr; fis_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU; fis_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU; fis_dma_obj.dma_attr.dma_attr_sgllen = 1; fis_dma_obj.dma_attr.dma_attr_align = 1; /* allocate kernel buffer for DMA */ if (mrsas_alloc_dma_obj(instance, &fis_dma_obj, (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) { con_log(CL_ANN, (CE_WARN, "issue_mfi_stp : " "could not allocate data transfer buffer.")); return (DDI_FAILURE); } (void) memset(fis_dma_obj.buffer, 0, fis_xferlen); /* If IOCTL requires DMA WRITE, do ddi_copyin IOCTL data copy */ for (i = 0; i < fis_xferlen; i++) { if (ddi_copyin((uint8_t *)fis_ubuf + i, (uint8_t *)fis_dma_obj.buffer + i, 1, mode)) { con_log(CL_ANN, (CE_WARN, "issue_mfi_stp: " "copy from user space failed")); return (DDI_FAILURE); } } } if (data_xferlen) { con_log(CL_ANN, (CE_CONT, "issue_mfi_stp: data_ubuf = %p " "data_xferlen = %x", data_ubuf, data_xferlen)); /* means IOCTL requires DMA */ /* allocate the data transfer buffer */ /* data_dma_obj.size = data_xferlen; */ MRSAS_GET_BOUNDARY_ALIGNED_LEN(data_xferlen, new_xfer_length2, PAGESIZE); data_dma_obj.size = new_xfer_length2; data_dma_obj.dma_attr = mrsas_generic_dma_attr; data_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU; data_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU; data_dma_obj.dma_attr.dma_attr_sgllen = 1; data_dma_obj.dma_attr.dma_attr_align = 1; /* allocate kernel buffer for DMA */ if (mrsas_alloc_dma_obj(instance, &data_dma_obj, (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) { con_log(CL_ANN, (CE_WARN, "issue_mfi_stp: " "could not allocate data transfer buffer.")); return (DDI_FAILURE); } (void) memset(data_dma_obj.buffer, 0, data_xferlen); /* If IOCTL requires DMA WRITE, do ddi_copyin IOCTL data copy */ for (i = 0; i < data_xferlen; i++) { if (ddi_copyin((uint8_t *)data_ubuf + i, (uint8_t *)data_dma_obj.buffer + i, 1, mode)) { con_log(CL_ANN, (CE_WARN, "issue_mfi_stp: " "copy from user space failed")); return (DDI_FAILURE); } } } ddi_put8(acc_handle, &stp->cmd, kstp->cmd); ddi_put8(acc_handle, &stp->cmd_status, 0); ddi_put8(acc_handle, &stp->connection_status, 0); ddi_put8(acc_handle, &stp->target_id, kstp->target_id); ddi_put8(acc_handle, &stp->sge_count, kstp->sge_count); ddi_put16(acc_handle, &stp->timeout, kstp->timeout); ddi_put32(acc_handle, &stp->data_xfer_len, kstp->data_xfer_len); ddi_rep_put8(acc_handle, (uint8_t *)kstp->fis, (uint8_t *)stp->fis, 10, DDI_DEV_AUTOINCR); ddi_put16(acc_handle, &stp->flags, kstp->flags & ~MFI_FRAME_SGL64); ddi_put32(acc_handle, &stp->stp_flags, kstp->stp_flags); ddi_put32(acc_handle, &stp->sgl.sge32[0].length, fis_xferlen); ddi_put32(acc_handle, &stp->sgl.sge32[0].phys_addr, fis_dma_obj.dma_cookie[0].dmac_address); ddi_put32(acc_handle, &stp->sgl.sge32[1].length, data_xferlen); ddi_put32(acc_handle, &stp->sgl.sge32[1].phys_addr, data_dma_obj.dma_cookie[0].dmac_address); cmd->sync_cmd = MRSAS_TRUE; cmd->frame_count = 1; if (instance->tbolt) { mr_sas_tbolt_build_mfi_cmd(instance, cmd); } if (instance->func_ptr->issue_cmd_in_sync_mode(instance, cmd)) { con_log(CL_ANN, (CE_WARN, "issue_mfi_stp: fw_ioctl failed")); } else { if (fis_xferlen) { for (i = 0; i < fis_xferlen; i++) { if (ddi_copyout( (uint8_t *)fis_dma_obj.buffer + i, (uint8_t *)fis_ubuf + i, 1, mode)) { con_log(CL_ANN, (CE_WARN, "issue_mfi_stp : copy to " "user space failed")); return (DDI_FAILURE); } } } } if (data_xferlen) { for (i = 0; i < data_xferlen; i++) { if (ddi_copyout( (uint8_t *)data_dma_obj.buffer + i, (uint8_t *)data_ubuf + i, 1, mode)) { con_log(CL_ANN, (CE_WARN, "issue_mfi_stp : copy to" " user space failed")); return (DDI_FAILURE); } } } kstp->cmd_status = ddi_get8(acc_handle, &stp->cmd_status); con_log(CL_ANN1, (CE_NOTE, "issue_mfi_stp: stp->cmd_status = %d", kstp->cmd_status)); DTRACE_PROBE2(issue_stp, uint8_t, kstp->cmd, uint8_t, kstp->cmd_status); if (fis_xferlen) { /* free kernel buffer */ if (mrsas_free_dma_obj(instance, fis_dma_obj) != DDI_SUCCESS) return (DDI_FAILURE); } if (data_xferlen) { /* free kernel buffer */ if (mrsas_free_dma_obj(instance, data_dma_obj) != DDI_SUCCESS) return (DDI_FAILURE); } return (DDI_SUCCESS); } /* * fill_up_drv_ver */ void fill_up_drv_ver(struct mrsas_drv_ver *dv) { (void) memset(dv, 0, sizeof (struct mrsas_drv_ver)); (void) memcpy(dv->signature, "$LSI LOGIC$", strlen("$LSI LOGIC$")); (void) memcpy(dv->os_name, "Solaris", strlen("Solaris")); (void) memcpy(dv->drv_name, "mr_sas", strlen("mr_sas")); (void) memcpy(dv->drv_ver, MRSAS_VERSION, strlen(MRSAS_VERSION)); (void) memcpy(dv->drv_rel_date, MRSAS_RELDATE, strlen(MRSAS_RELDATE)); } /* * handle_drv_ioctl */ static int handle_drv_ioctl(struct mrsas_instance *instance, struct mrsas_ioctl *ioctl, int mode) { int i; int rval = DDI_SUCCESS; int *props = NULL; void *ubuf; uint8_t *pci_conf_buf; uint32_t xferlen; uint32_t num_props; uint_t model; struct mrsas_dcmd_frame *kdcmd; struct mrsas_drv_ver dv; struct mrsas_pci_information pi; kdcmd = (struct mrsas_dcmd_frame *)&ioctl->frame[0]; model = ddi_model_convert_from(mode & FMODELS); if (model == DDI_MODEL_ILP32) { con_log(CL_ANN1, (CE_CONT, "handle_drv_ioctl: DDI_MODEL_ILP32")); xferlen = kdcmd->sgl.sge32[0].length; ubuf = (void *)(ulong_t)kdcmd->sgl.sge32[0].phys_addr; } else { #ifdef _ILP32 con_log(CL_ANN1, (CE_CONT, "handle_drv_ioctl: DDI_MODEL_ILP32")); xferlen = kdcmd->sgl.sge32[0].length; ubuf = (void *)(ulong_t)kdcmd->sgl.sge32[0].phys_addr; #else con_log(CL_ANN1, (CE_CONT, "handle_drv_ioctl: DDI_MODEL_LP64")); xferlen = kdcmd->sgl.sge64[0].length; ubuf = (void *)(ulong_t)kdcmd->sgl.sge64[0].phys_addr; #endif } con_log(CL_ANN1, (CE_CONT, "handle_drv_ioctl: " "dataBuf=%p size=%d bytes", ubuf, xferlen)); switch (kdcmd->opcode) { case MRSAS_DRIVER_IOCTL_DRIVER_VERSION: con_log(CL_ANN1, (CE_CONT, "handle_drv_ioctl: " "MRSAS_DRIVER_IOCTL_DRIVER_VERSION")); fill_up_drv_ver(&dv); if (ddi_copyout(&dv, ubuf, xferlen, mode)) { con_log(CL_ANN, (CE_WARN, "handle_drv_ioctl: " "MRSAS_DRIVER_IOCTL_DRIVER_VERSION : " "copy to user space failed")); kdcmd->cmd_status = 1; rval = 1; } else { kdcmd->cmd_status = 0; } break; case MRSAS_DRIVER_IOCTL_PCI_INFORMATION: con_log(CL_ANN1, (CE_NOTE, "handle_drv_ioctl: " "MRSAS_DRIVER_IOCTL_PCI_INFORMAITON")); if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, instance->dip, 0, "reg", &props, &num_props)) { con_log(CL_ANN, (CE_WARN, "handle_drv_ioctl: " "MRSAS_DRIVER_IOCTL_PCI_INFORMATION : " "ddi_prop_look_int_array failed")); rval = DDI_FAILURE; } else { pi.busNumber = (props[0] >> 16) & 0xFF; pi.deviceNumber = (props[0] >> 11) & 0x1f; pi.functionNumber = (props[0] >> 8) & 0x7; ddi_prop_free((void *)props); } pci_conf_buf = (uint8_t *)&pi.pciHeaderInfo; for (i = 0; i < (sizeof (struct mrsas_pci_information) - offsetof(struct mrsas_pci_information, pciHeaderInfo)); i++) { pci_conf_buf[i] = pci_config_get8(instance->pci_handle, i); } if (ddi_copyout(&pi, ubuf, xferlen, mode)) { con_log(CL_ANN, (CE_WARN, "handle_drv_ioctl: " "MRSAS_DRIVER_IOCTL_PCI_INFORMATION : " "copy to user space failed")); kdcmd->cmd_status = 1; rval = 1; } else { kdcmd->cmd_status = 0; } break; default: con_log(CL_ANN, (CE_WARN, "handle_drv_ioctl: " "invalid driver specific IOCTL opcode = 0x%x", kdcmd->opcode)); kdcmd->cmd_status = 1; rval = DDI_FAILURE; break; } return (rval); } /* * handle_mfi_ioctl */ static int handle_mfi_ioctl(struct mrsas_instance *instance, struct mrsas_ioctl *ioctl, int mode) { int rval = DDI_SUCCESS; struct mrsas_header *hdr; struct mrsas_cmd *cmd; if (instance->tbolt) { cmd = get_raid_msg_mfi_pkt(instance); } else { cmd = mrsas_get_mfi_pkt(instance); } if (!cmd) { con_log(CL_ANN, (CE_WARN, "mr_sas: " "failed to get a cmd packet")); DTRACE_PROBE2(mfi_ioctl_err, uint16_t, instance->fw_outstanding, uint16_t, instance->max_fw_cmds); return (DDI_FAILURE); } /* Clear the frame buffer and assign back the context id */ (void) memset((char *)&cmd->frame[0], 0, sizeof (union mrsas_frame)); ddi_put32(cmd->frame_dma_obj.acc_handle, &cmd->frame->hdr.context, cmd->index); hdr = (struct mrsas_header *)&ioctl->frame[0]; switch (ddi_get8(cmd->frame_dma_obj.acc_handle, &hdr->cmd)) { case MFI_CMD_OP_DCMD: rval = issue_mfi_dcmd(instance, ioctl, cmd, mode); break; case MFI_CMD_OP_SMP: rval = issue_mfi_smp(instance, ioctl, cmd, mode); break; case MFI_CMD_OP_STP: rval = issue_mfi_stp(instance, ioctl, cmd, mode); break; case MFI_CMD_OP_LD_SCSI: case MFI_CMD_OP_PD_SCSI: rval = issue_mfi_pthru(instance, ioctl, cmd, mode); break; default: con_log(CL_ANN, (CE_WARN, "handle_mfi_ioctl: " "invalid mfi ioctl hdr->cmd = %d", hdr->cmd)); rval = DDI_FAILURE; break; } if (mrsas_common_check(instance, cmd) != DDI_SUCCESS) rval = DDI_FAILURE; if (instance->tbolt) { return_raid_msg_mfi_pkt(instance, cmd); } else { mrsas_return_mfi_pkt(instance, cmd); } return (rval); } /* * AEN */ static int handle_mfi_aen(struct mrsas_instance *instance, struct mrsas_aen *aen) { int rval = 0; rval = register_mfi_aen(instance, instance->aen_seq_num, aen->class_locale_word); aen->cmd_status = (uint8_t)rval; return (rval); } static int register_mfi_aen(struct mrsas_instance *instance, uint32_t seq_num, uint32_t class_locale_word) { int ret_val; struct mrsas_cmd *cmd, *aen_cmd; struct mrsas_dcmd_frame *dcmd; union mrsas_evt_class_locale curr_aen; union mrsas_evt_class_locale prev_aen; con_log(CL_ANN, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); /* * If there an AEN pending already (aen_cmd), check if the * class_locale of that pending AEN is inclusive of the new * AEN request we currently have. If it is, then we don't have * to do anything. In other words, whichever events the current * AEN request is subscribing to, have already been subscribed * to. * * If the old_cmd is _not_ inclusive, then we have to abort * that command, form a class_locale that is superset of both * old and current and re-issue to the FW */ curr_aen.word = LE_32(class_locale_word); curr_aen.members.locale = LE_16(curr_aen.members.locale); aen_cmd = instance->aen_cmd; if (aen_cmd) { prev_aen.word = ddi_get32(aen_cmd->frame_dma_obj.acc_handle, &aen_cmd->frame->dcmd.mbox.w[1]); prev_aen.word = LE_32(prev_aen.word); prev_aen.members.locale = LE_16(prev_aen.members.locale); /* * A class whose enum value is smaller is inclusive of all * higher values. If a PROGRESS (= -1) was previously * registered, then a new registration requests for higher * classes need not be sent to FW. They are automatically * included. * * Locale numbers don't have such hierarchy. They are bitmap * values */ if ((prev_aen.members.class <= curr_aen.members.class) && !((prev_aen.members.locale & curr_aen.members.locale) ^ curr_aen.members.locale)) { /* * Previously issued event registration includes * current request. Nothing to do. */ return (0); } else { curr_aen.members.locale |= prev_aen.members.locale; if (prev_aen.members.class < curr_aen.members.class) curr_aen.members.class = prev_aen.members.class; ret_val = abort_aen_cmd(instance, aen_cmd); if (ret_val) { con_log(CL_ANN, (CE_WARN, "register_mfi_aen: " "failed to abort prevous AEN command")); return (ret_val); } } } else { curr_aen.word = LE_32(class_locale_word); curr_aen.members.locale = LE_16(curr_aen.members.locale); } if (instance->tbolt) { cmd = get_raid_msg_mfi_pkt(instance); } else { cmd = mrsas_get_mfi_pkt(instance); } if (!cmd) { DTRACE_PROBE2(mfi_aen_err, uint16_t, instance->fw_outstanding, uint16_t, instance->max_fw_cmds); return (ENOMEM); } /* Clear the frame buffer and assign back the context id */ (void) memset((char *)&cmd->frame[0], 0, sizeof (union mrsas_frame)); ddi_put32(cmd->frame_dma_obj.acc_handle, &cmd->frame->hdr.context, cmd->index); dcmd = &cmd->frame->dcmd; /* for(i = 0; i < DCMD_MBOX_SZ; i++) dcmd->mbox.b[i] = 0; */ (void) memset(dcmd->mbox.b, 0, DCMD_MBOX_SZ); (void) memset(instance->mfi_evt_detail_obj.buffer, 0, sizeof (struct mrsas_evt_detail)); /* Prepare DCMD for aen registration */ ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->cmd, MFI_CMD_OP_DCMD); ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->cmd_status, 0x0); ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->sge_count, 1); ddi_put16(cmd->frame_dma_obj.acc_handle, &dcmd->flags, MFI_FRAME_DIR_READ); ddi_put16(cmd->frame_dma_obj.acc_handle, &dcmd->timeout, 0); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->data_xfer_len, sizeof (struct mrsas_evt_detail)); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->opcode, MR_DCMD_CTRL_EVENT_WAIT); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->mbox.w[0], seq_num); curr_aen.members.locale = LE_16(curr_aen.members.locale); curr_aen.word = LE_32(curr_aen.word); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->mbox.w[1], curr_aen.word); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->sgl.sge32[0].phys_addr, instance->mfi_evt_detail_obj.dma_cookie[0].dmac_address); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->sgl.sge32[0].length, sizeof (struct mrsas_evt_detail)); instance->aen_seq_num = seq_num; /* * Store reference to the cmd used to register for AEN. When an * application wants us to register for AEN, we have to abort this * cmd and re-register with a new EVENT LOCALE supplied by that app */ instance->aen_cmd = cmd; cmd->frame_count = 1; /* Issue the aen registration frame */ /* atomic_add_16 (&instance->fw_outstanding, 1); */ if (instance->tbolt) { mr_sas_tbolt_build_mfi_cmd(instance, cmd); } instance->func_ptr->issue_cmd(cmd, instance); return (0); } void display_scsi_inquiry(caddr_t scsi_inq) { #define MAX_SCSI_DEVICE_CODE 14 int i; char inquiry_buf[256] = {0}; int len; const char *const scsi_device_types[] = { "Direct-Access ", "Sequential-Access", "Printer ", "Processor ", "WORM ", "CD-ROM ", "Scanner ", "Optical Device ", "Medium Changer ", "Communications ", "Unknown ", "Unknown ", "Unknown ", "Enclosure ", }; len = 0; len += snprintf(inquiry_buf + len, 265 - len, " Vendor: "); for (i = 8; i < 16; i++) { len += snprintf(inquiry_buf + len, 265 - len, "%c", scsi_inq[i]); } len += snprintf(inquiry_buf + len, 265 - len, " Model: "); for (i = 16; i < 32; i++) { len += snprintf(inquiry_buf + len, 265 - len, "%c", scsi_inq[i]); } len += snprintf(inquiry_buf + len, 265 - len, " Rev: "); for (i = 32; i < 36; i++) { len += snprintf(inquiry_buf + len, 265 - len, "%c", scsi_inq[i]); } len += snprintf(inquiry_buf + len, 265 - len, "\n"); i = scsi_inq[0] & 0x1f; len += snprintf(inquiry_buf + len, 265 - len, " Type: %s ", i < MAX_SCSI_DEVICE_CODE ? scsi_device_types[i] : "Unknown "); len += snprintf(inquiry_buf + len, 265 - len, " ANSI SCSI revision: %02x", scsi_inq[2] & 0x07); if ((scsi_inq[2] & 0x07) == 1 && (scsi_inq[3] & 0x0f) == 1) { len += snprintf(inquiry_buf + len, 265 - len, " CCS\n"); } else { len += snprintf(inquiry_buf + len, 265 - len, "\n"); } con_log(CL_DLEVEL2, (CE_CONT, inquiry_buf)); } static void io_timeout_checker(void *arg) { struct scsi_pkt *pkt; struct mrsas_instance *instance = arg; struct mrsas_cmd *cmd = NULL; struct mrsas_header *hdr; int time = 0; int counter = 0; struct mlist_head *pos, *next; mlist_t process_list; if (instance->adapterresetinprogress == 1) { con_log(CL_ANN, (CE_NOTE, "io_timeout_checker:" " reset in progress")); instance->timeout_id = timeout(io_timeout_checker, (void *) instance, drv_usectohz(MRSAS_1_SECOND)); return; } /* See if this check needs to be in the beginning or last in ISR */ if (mrsas_initiate_ocr_if_fw_is_faulty(instance) == 1) { dev_err(instance->dip, CE_WARN, "io_timeout_checker: " "FW Fault, calling reset adapter"); dev_err(instance->dip, CE_CONT, "io_timeout_checker: " "fw_outstanding 0x%X max_fw_cmds 0x%X", instance->fw_outstanding, instance->max_fw_cmds); if (instance->adapterresetinprogress == 0) { instance->adapterresetinprogress = 1; if (instance->tbolt) (void) mrsas_tbolt_reset_ppc(instance); else (void) mrsas_reset_ppc(instance); instance->adapterresetinprogress = 0; } instance->timeout_id = timeout(io_timeout_checker, (void *) instance, drv_usectohz(MRSAS_1_SECOND)); return; } INIT_LIST_HEAD(&process_list); mutex_enter(&instance->cmd_pend_mtx); mlist_for_each_safe(pos, next, &instance->cmd_pend_list) { cmd = mlist_entry(pos, struct mrsas_cmd, list); if (cmd == NULL) { continue; } if (cmd->sync_cmd == MRSAS_TRUE) { hdr = (struct mrsas_header *)&cmd->frame->hdr; if (hdr == NULL) { continue; } time = --cmd->drv_pkt_time; } else { pkt = cmd->pkt; if (pkt == NULL) { continue; } time = --cmd->drv_pkt_time; } if (time <= 0) { dev_err(instance->dip, CE_WARN, "%llx: " "io_timeout_checker: TIMING OUT: pkt: %p, " "cmd %p fw_outstanding 0x%X max_fw_cmds 0x%X", gethrtime(), (void *)pkt, (void *)cmd, instance->fw_outstanding, instance->max_fw_cmds); counter++; break; } } mutex_exit(&instance->cmd_pend_mtx); if (counter) { if (instance->disable_online_ctrl_reset == 1) { dev_err(instance->dip, CE_WARN, "%s(): OCR is NOT " "supported by Firmware, KILL adapter!!!", __func__); if (instance->tbolt) mrsas_tbolt_kill_adapter(instance); else (void) mrsas_kill_adapter(instance); return; } else { if (cmd->retry_count_for_ocr <= IO_RETRY_COUNT) { if (instance->adapterresetinprogress == 0) { if (instance->tbolt) { (void) mrsas_tbolt_reset_ppc( instance); } else { (void) mrsas_reset_ppc( instance); } } } else { dev_err(instance->dip, CE_WARN, "io_timeout_checker: " "cmd %p cmd->index %d " "timed out even after 3 resets: " "so KILL adapter", (void *)cmd, cmd->index); mrsas_print_cmd_details(instance, cmd, 0xDD); if (instance->tbolt) mrsas_tbolt_kill_adapter(instance); else (void) mrsas_kill_adapter(instance); return; } } } con_log(CL_ANN, (CE_NOTE, "mrsas: " "schedule next timeout check: " "do timeout \n")); instance->timeout_id = timeout(io_timeout_checker, (void *)instance, drv_usectohz(MRSAS_1_SECOND)); } static uint32_t read_fw_status_reg_ppc(struct mrsas_instance *instance) { return ((uint32_t)RD_OB_SCRATCH_PAD_0(instance)); } static void issue_cmd_ppc(struct mrsas_cmd *cmd, struct mrsas_instance *instance) { struct scsi_pkt *pkt; atomic_inc_16(&instance->fw_outstanding); pkt = cmd->pkt; if (pkt) { con_log(CL_DLEVEL1, (CE_NOTE, "%llx : issue_cmd_ppc:" "ISSUED CMD TO FW : called : cmd:" ": %p instance : %p pkt : %p pkt_time : %x\n", gethrtime(), (void *)cmd, (void *)instance, (void *)pkt, cmd->drv_pkt_time)); if (instance->adapterresetinprogress) { cmd->drv_pkt_time = (uint16_t)debug_timeout_g; con_log(CL_ANN1, (CE_NOTE, "Reset the scsi_pkt timer")); } else { push_pending_mfi_pkt(instance, cmd); } } else { con_log(CL_DLEVEL1, (CE_NOTE, "%llx : issue_cmd_ppc:" "ISSUED CMD TO FW : called : cmd : %p, instance: %p" "(NO PKT)\n", gethrtime(), (void *)cmd, (void *)instance)); } mutex_enter(&instance->reg_write_mtx); /* Issue the command to the FW */ WR_IB_PICK_QPORT((cmd->frame_phys_addr) | (((cmd->frame_count - 1) << 1) | 1), instance); mutex_exit(&instance->reg_write_mtx); } /* * issue_cmd_in_sync_mode */ static int issue_cmd_in_sync_mode_ppc(struct mrsas_instance *instance, struct mrsas_cmd *cmd) { int i; uint32_t msecs = MFI_POLL_TIMEOUT_SECS * MILLISEC; struct mrsas_header *hdr = &cmd->frame->hdr; con_log(CL_ANN1, (CE_NOTE, "issue_cmd_in_sync_mode_ppc: called")); if (instance->adapterresetinprogress) { cmd->drv_pkt_time = ddi_get16( cmd->frame_dma_obj.acc_handle, &hdr->timeout); if (cmd->drv_pkt_time < debug_timeout_g) cmd->drv_pkt_time = (uint16_t)debug_timeout_g; con_log(CL_ANN1, (CE_NOTE, "sync_mode_ppc: " "issue and return in reset case\n")); WR_IB_PICK_QPORT((cmd->frame_phys_addr) | (((cmd->frame_count - 1) << 1) | 1), instance); return (DDI_SUCCESS); } else { con_log(CL_ANN1, (CE_NOTE, "sync_mode_ppc: pushing the pkt\n")); push_pending_mfi_pkt(instance, cmd); } cmd->cmd_status = ENODATA; mutex_enter(&instance->reg_write_mtx); /* Issue the command to the FW */ WR_IB_PICK_QPORT((cmd->frame_phys_addr) | (((cmd->frame_count - 1) << 1) | 1), instance); mutex_exit(&instance->reg_write_mtx); mutex_enter(&instance->int_cmd_mtx); for (i = 0; i < msecs && (cmd->cmd_status == ENODATA); i++) { cv_wait(&instance->int_cmd_cv, &instance->int_cmd_mtx); } mutex_exit(&instance->int_cmd_mtx); con_log(CL_ANN1, (CE_NOTE, "issue_cmd_in_sync_mode_ppc: done")); if (i < (msecs -1)) { return (DDI_SUCCESS); } else { return (DDI_FAILURE); } } /* * issue_cmd_in_poll_mode */ static int issue_cmd_in_poll_mode_ppc(struct mrsas_instance *instance, struct mrsas_cmd *cmd) { int i; uint16_t flags; uint32_t msecs = MFI_POLL_TIMEOUT_SECS * MILLISEC; struct mrsas_header *frame_hdr; con_log(CL_ANN1, (CE_NOTE, "issue_cmd_in_poll_mode_ppc: called")); frame_hdr = (struct mrsas_header *)cmd->frame; ddi_put8(cmd->frame_dma_obj.acc_handle, &frame_hdr->cmd_status, MFI_CMD_STATUS_POLL_MODE); flags = ddi_get16(cmd->frame_dma_obj.acc_handle, &frame_hdr->flags); flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE; ddi_put16(cmd->frame_dma_obj.acc_handle, &frame_hdr->flags, flags); /* issue the frame using inbound queue port */ WR_IB_PICK_QPORT((cmd->frame_phys_addr) | (((cmd->frame_count - 1) << 1) | 1), instance); /* wait for cmd_status to change from 0xFF */ for (i = 0; i < msecs && ( ddi_get8(cmd->frame_dma_obj.acc_handle, &frame_hdr->cmd_status) == MFI_CMD_STATUS_POLL_MODE); i++) { drv_usecwait(MILLISEC); /* wait for 1000 usecs */ } if (ddi_get8(cmd->frame_dma_obj.acc_handle, &frame_hdr->cmd_status) == MFI_CMD_STATUS_POLL_MODE) { con_log(CL_ANN, (CE_NOTE, "issue_cmd_in_poll_mode: " "cmd polling timed out")); return (DDI_FAILURE); } return (DDI_SUCCESS); } static void enable_intr_ppc(struct mrsas_instance *instance) { uint32_t mask; con_log(CL_ANN1, (CE_NOTE, "enable_intr_ppc: called")); if (instance->skinny) { /* For SKINNY, write ~0x1, from BSD's mfi driver. */ WR_OB_INTR_MASK(0xfffffffe, instance); } else { /* WR_OB_DOORBELL_CLEAR(0xFFFFFFFF, instance); */ WR_OB_DOORBELL_CLEAR(OB_DOORBELL_CLEAR_MASK, instance); /* WR_OB_INTR_MASK(~0x80000000, instance); */ WR_OB_INTR_MASK(~(MFI_REPLY_2108_MESSAGE_INTR_MASK), instance); } /* dummy read to force PCI flush */ mask = RD_OB_INTR_MASK(instance); con_log(CL_ANN1, (CE_NOTE, "enable_intr_ppc: " "outbound_intr_mask = 0x%x", mask)); } static void disable_intr_ppc(struct mrsas_instance *instance) { con_log(CL_ANN1, (CE_NOTE, "disable_intr_ppc: called")); con_log(CL_ANN1, (CE_NOTE, "disable_intr_ppc: before : " "outbound_intr_mask = 0x%x", RD_OB_INTR_MASK(instance))); /* For now, assume there are no extras needed for Skinny support. */ WR_OB_INTR_MASK(OB_INTR_MASK, instance); con_log(CL_ANN1, (CE_NOTE, "disable_intr_ppc: after : " "outbound_intr_mask = 0x%x", RD_OB_INTR_MASK(instance))); /* dummy read to force PCI flush */ (void) RD_OB_INTR_MASK(instance); } static int intr_ack_ppc(struct mrsas_instance *instance) { uint32_t status; int ret = DDI_INTR_CLAIMED; con_log(CL_ANN1, (CE_NOTE, "intr_ack_ppc: called")); /* check if it is our interrupt */ status = RD_OB_INTR_STATUS(instance); con_log(CL_ANN1, (CE_NOTE, "intr_ack_ppc: status = 0x%x", status)); /* * NOTE: Some drivers call out SKINNY here, but the return is the same * for SKINNY and 2108. */ if (!(status & MFI_REPLY_2108_MESSAGE_INTR)) { ret = DDI_INTR_UNCLAIMED; } if (mrsas_check_acc_handle(instance->regmap_handle) != DDI_SUCCESS) { ddi_fm_service_impact(instance->dip, DDI_SERVICE_LOST); ret = DDI_INTR_UNCLAIMED; } if (ret == DDI_INTR_UNCLAIMED) { return (ret); } /* * Clear the interrupt by writing back the same value. * Another case where SKINNY is slightly different. */ if (instance->skinny) { WR_OB_INTR_STATUS(status, instance); } else { WR_OB_DOORBELL_CLEAR(status, instance); } /* dummy READ */ status = RD_OB_INTR_STATUS(instance); con_log(CL_ANN1, (CE_NOTE, "intr_ack_ppc: interrupt cleared")); return (ret); } /* * Marks HBA as bad. This will be called either when an * IO packet times out even after 3 FW resets * or FW is found to be fault even after 3 continuous resets. */ static int mrsas_kill_adapter(struct mrsas_instance *instance) { if (instance->deadadapter == 1) return (DDI_FAILURE); con_log(CL_ANN1, (CE_NOTE, "mrsas_kill_adapter: " "Writing to doorbell with MFI_STOP_ADP ")); mutex_enter(&instance->ocr_flags_mtx); instance->deadadapter = 1; mutex_exit(&instance->ocr_flags_mtx); instance->func_ptr->disable_intr(instance); WR_IB_DOORBELL(MFI_STOP_ADP, instance); (void) mrsas_complete_pending_cmds(instance); return (DDI_SUCCESS); } static int mrsas_reset_ppc(struct mrsas_instance *instance) { uint32_t status; uint32_t retry = 0; uint32_t cur_abs_reg_val; uint32_t fw_state; con_log(CL_ANN, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); if (instance->deadadapter == 1) { dev_err(instance->dip, CE_WARN, "mrsas_reset_ppc: " "no more resets as HBA has been marked dead "); return (DDI_FAILURE); } mutex_enter(&instance->ocr_flags_mtx); instance->adapterresetinprogress = 1; mutex_exit(&instance->ocr_flags_mtx); con_log(CL_ANN1, (CE_NOTE, "mrsas_reset_ppc: adpterresetinprogress " "flag set, time %llx", gethrtime())); instance->func_ptr->disable_intr(instance); retry_reset: WR_IB_WRITE_SEQ(0, instance); WR_IB_WRITE_SEQ(4, instance); WR_IB_WRITE_SEQ(0xb, instance); WR_IB_WRITE_SEQ(2, instance); WR_IB_WRITE_SEQ(7, instance); WR_IB_WRITE_SEQ(0xd, instance); con_log(CL_ANN1, (CE_NOTE, "mrsas_reset_ppc: magic number written " "to write sequence register\n")); delay(100 * drv_usectohz(MILLISEC)); status = RD_OB_DRWE(instance); while (!(status & DIAG_WRITE_ENABLE)) { delay(100 * drv_usectohz(MILLISEC)); status = RD_OB_DRWE(instance); if (retry++ == 100) { dev_err(instance->dip, CE_WARN, "mrsas_reset_ppc: DRWE bit " "check retry count %d", retry); return (DDI_FAILURE); } } WR_IB_DRWE(status | DIAG_RESET_ADAPTER, instance); delay(100 * drv_usectohz(MILLISEC)); status = RD_OB_DRWE(instance); while (status & DIAG_RESET_ADAPTER) { delay(100 * drv_usectohz(MILLISEC)); status = RD_OB_DRWE(instance); if (retry++ == 100) { dev_err(instance->dip, CE_WARN, "mrsas_reset_ppc: " "RESET FAILED. KILL adapter called."); (void) mrsas_kill_adapter(instance); return (DDI_FAILURE); } } con_log(CL_ANN, (CE_NOTE, "mrsas_reset_ppc: Adapter reset complete")); con_log(CL_ANN1, (CE_NOTE, "mrsas_reset_ppc: " "Calling mfi_state_transition_to_ready")); /* Mark HBA as bad, if FW is fault after 3 continuous resets */ if (mfi_state_transition_to_ready(instance) || debug_fw_faults_after_ocr_g == 1) { cur_abs_reg_val = instance->func_ptr->read_fw_status_reg(instance); fw_state = cur_abs_reg_val & MFI_STATE_MASK; #ifdef OCRDEBUG con_log(CL_ANN1, (CE_NOTE, "mrsas_reset_ppc :before fake: FW is not ready " "FW state = 0x%x", fw_state)); if (debug_fw_faults_after_ocr_g == 1) fw_state = MFI_STATE_FAULT; #endif con_log(CL_ANN1, (CE_NOTE, "mrsas_reset_ppc : FW is not ready " "FW state = 0x%x", fw_state)); if (fw_state == MFI_STATE_FAULT) { /* increment the count */ instance->fw_fault_count_after_ocr++; if (instance->fw_fault_count_after_ocr < MAX_FW_RESET_COUNT) { dev_err(instance->dip, CE_WARN, "mrsas_reset_ppc: " "FW is in fault after OCR count %d " "Retry Reset", instance->fw_fault_count_after_ocr); goto retry_reset; } else { dev_err(instance->dip, CE_WARN, "mrsas_reset_ppc: " "Max Reset Count exceeded >%d" "Mark HBA as bad, KILL adapter", MAX_FW_RESET_COUNT); (void) mrsas_kill_adapter(instance); return (DDI_FAILURE); } } } /* reset the counter as FW is up after OCR */ instance->fw_fault_count_after_ocr = 0; ddi_put32(instance->mfi_internal_dma_obj.acc_handle, instance->producer, 0); ddi_put32(instance->mfi_internal_dma_obj.acc_handle, instance->consumer, 0); con_log(CL_ANN1, (CE_NOTE, "mrsas_reset_ppc: " " after resetting produconsumer chck indexs:" "producer %x consumer %x", *instance->producer, *instance->consumer)); con_log(CL_ANN1, (CE_NOTE, "mrsas_reset_ppc: " "Calling mrsas_issue_init_mfi")); (void) mrsas_issue_init_mfi(instance); con_log(CL_ANN1, (CE_NOTE, "mrsas_reset_ppc: " "mrsas_issue_init_mfi Done")); con_log(CL_ANN1, (CE_NOTE, "mrsas_reset_ppc: " "Calling mrsas_print_pending_cmd\n")); (void) mrsas_print_pending_cmds(instance); con_log(CL_ANN1, (CE_NOTE, "mrsas_reset_ppc: " "mrsas_print_pending_cmd done\n")); instance->func_ptr->enable_intr(instance); instance->fw_outstanding = 0; con_log(CL_ANN1, (CE_NOTE, "mrsas_reset_ppc: " "Calling mrsas_issue_pending_cmds")); (void) mrsas_issue_pending_cmds(instance); con_log(CL_ANN1, (CE_NOTE, "mrsas_reset_ppc: " "issue_pending_cmds done.\n")); con_log(CL_ANN1, (CE_NOTE, "mrsas_reset_ppc: " "Calling aen registration")); instance->aen_cmd->retry_count_for_ocr = 0; instance->aen_cmd->drv_pkt_time = 0; instance->func_ptr->issue_cmd(instance->aen_cmd, instance); con_log(CL_ANN1, (CE_NOTE, "Unsetting adpresetinprogress flag.\n")); mutex_enter(&instance->ocr_flags_mtx); instance->adapterresetinprogress = 0; mutex_exit(&instance->ocr_flags_mtx); con_log(CL_ANN1, (CE_NOTE, "mrsas_reset_ppc: " "adpterresetinprogress flag unset")); con_log(CL_ANN1, (CE_NOTE, "mrsas_reset_ppc done\n")); return (DDI_SUCCESS); } /* * FMA functions. */ int mrsas_common_check(struct mrsas_instance *instance, struct mrsas_cmd *cmd) { int ret = DDI_SUCCESS; if (cmd != NULL && mrsas_check_dma_handle(cmd->frame_dma_obj.dma_handle) != DDI_SUCCESS) { ddi_fm_service_impact(instance->dip, DDI_SERVICE_UNAFFECTED); if (cmd->pkt != NULL) { cmd->pkt->pkt_reason = CMD_TRAN_ERR; cmd->pkt->pkt_statistics = 0; } ret = DDI_FAILURE; } if (mrsas_check_dma_handle(instance->mfi_internal_dma_obj.dma_handle) != DDI_SUCCESS) { ddi_fm_service_impact(instance->dip, DDI_SERVICE_UNAFFECTED); if (cmd != NULL && cmd->pkt != NULL) { cmd->pkt->pkt_reason = CMD_TRAN_ERR; cmd->pkt->pkt_statistics = 0; } ret = DDI_FAILURE; } if (mrsas_check_dma_handle(instance->mfi_evt_detail_obj.dma_handle) != DDI_SUCCESS) { ddi_fm_service_impact(instance->dip, DDI_SERVICE_UNAFFECTED); if (cmd != NULL && cmd->pkt != NULL) { cmd->pkt->pkt_reason = CMD_TRAN_ERR; cmd->pkt->pkt_statistics = 0; } ret = DDI_FAILURE; } if (mrsas_check_acc_handle(instance->regmap_handle) != DDI_SUCCESS) { ddi_fm_service_impact(instance->dip, DDI_SERVICE_UNAFFECTED); ddi_fm_acc_err_clear(instance->regmap_handle, DDI_FME_VER0); if (cmd != NULL && cmd->pkt != NULL) { cmd->pkt->pkt_reason = CMD_TRAN_ERR; cmd->pkt->pkt_statistics = 0; } ret = DDI_FAILURE; } return (ret); } /*ARGSUSED*/ static int mrsas_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data) { /* * as the driver can always deal with an error in any dma or * access handle, we can just return the fme_status value. */ pci_ereport_post(dip, err, NULL); return (err->fme_status); } static void mrsas_fm_init(struct mrsas_instance *instance) { /* Need to change iblock to priority for new MSI intr */ ddi_iblock_cookie_t fm_ibc; /* Only register with IO Fault Services if we have some capability */ if (instance->fm_capabilities) { /* Adjust access and dma attributes for FMA */ endian_attr.devacc_attr_access = DDI_FLAGERR_ACC; mrsas_generic_dma_attr.dma_attr_flags = DDI_DMA_FLAGERR; /* * Register capabilities with IO Fault Services. * fm_capabilities will be updated to indicate * capabilities actually supported (not requested.) */ ddi_fm_init(instance->dip, &instance->fm_capabilities, &fm_ibc); /* * Initialize pci ereport capabilities if ereport * capable (should always be.) */ if (DDI_FM_EREPORT_CAP(instance->fm_capabilities) || DDI_FM_ERRCB_CAP(instance->fm_capabilities)) { pci_ereport_setup(instance->dip); } /* * Register error callback if error callback capable. */ if (DDI_FM_ERRCB_CAP(instance->fm_capabilities)) { ddi_fm_handler_register(instance->dip, mrsas_fm_error_cb, (void*) instance); } } else { endian_attr.devacc_attr_access = DDI_DEFAULT_ACC; mrsas_generic_dma_attr.dma_attr_flags = 0; } } static void mrsas_fm_fini(struct mrsas_instance *instance) { /* Only unregister FMA capabilities if registered */ if (instance->fm_capabilities) { /* * Un-register error callback if error callback capable. */ if (DDI_FM_ERRCB_CAP(instance->fm_capabilities)) { ddi_fm_handler_unregister(instance->dip); } /* * Release any resources allocated by pci_ereport_setup() */ if (DDI_FM_EREPORT_CAP(instance->fm_capabilities) || DDI_FM_ERRCB_CAP(instance->fm_capabilities)) { pci_ereport_teardown(instance->dip); } /* Unregister from IO Fault Services */ ddi_fm_fini(instance->dip); /* Adjust access and dma attributes for FMA */ endian_attr.devacc_attr_access = DDI_DEFAULT_ACC; mrsas_generic_dma_attr.dma_attr_flags = 0; } } int mrsas_check_acc_handle(ddi_acc_handle_t handle) { ddi_fm_error_t de; if (handle == NULL) { return (DDI_FAILURE); } ddi_fm_acc_err_get(handle, &de, DDI_FME_VERSION); return (de.fme_status); } int mrsas_check_dma_handle(ddi_dma_handle_t handle) { ddi_fm_error_t de; if (handle == NULL) { return (DDI_FAILURE); } ddi_fm_dma_err_get(handle, &de, DDI_FME_VERSION); return (de.fme_status); } void mrsas_fm_ereport(struct mrsas_instance *instance, char *detail) { uint64_t ena; char buf[FM_MAX_CLASS]; (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, detail); ena = fm_ena_generate(0, FM_ENA_FMT1); if (DDI_FM_EREPORT_CAP(instance->fm_capabilities)) { ddi_fm_ereport_post(instance->dip, buf, ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERSION, NULL); } } static int mrsas_add_intrs(struct mrsas_instance *instance, int intr_type) { dev_info_t *dip = instance->dip; int avail, actual, count; int i, flag, ret; con_log(CL_DLEVEL1, (CE_NOTE, "mrsas_add_intrs: intr_type = %x", intr_type)); /* Get number of interrupts */ ret = ddi_intr_get_nintrs(dip, intr_type, &count); if ((ret != DDI_SUCCESS) || (count == 0)) { con_log(CL_ANN, (CE_WARN, "ddi_intr_get_nintrs() failed:" "ret %d count %d", ret, count)); return (DDI_FAILURE); } con_log(CL_DLEVEL1, (CE_NOTE, "mrsas_add_intrs: count = %d ", count)); /* Get number of available interrupts */ ret = ddi_intr_get_navail(dip, intr_type, &avail); if ((ret != DDI_SUCCESS) || (avail == 0)) { con_log(CL_ANN, (CE_WARN, "ddi_intr_get_navail() failed:" "ret %d avail %d", ret, avail)); return (DDI_FAILURE); } con_log(CL_DLEVEL1, (CE_NOTE, "mrsas_add_intrs: avail = %d ", avail)); /* Only one interrupt routine. So limit the count to 1 */ if (count > 1) { count = 1; } /* * Allocate an array of interrupt handlers. Currently we support * only one interrupt. The framework can be extended later. */ instance->intr_htable_size = count * sizeof (ddi_intr_handle_t); instance->intr_htable = kmem_zalloc(instance->intr_htable_size, KM_SLEEP); ASSERT(instance->intr_htable); flag = ((intr_type == DDI_INTR_TYPE_MSI) || (intr_type == DDI_INTR_TYPE_MSIX)) ? DDI_INTR_ALLOC_STRICT : DDI_INTR_ALLOC_NORMAL; /* Allocate interrupt */ ret = ddi_intr_alloc(dip, instance->intr_htable, intr_type, 0, count, &actual, flag); if ((ret != DDI_SUCCESS) || (actual == 0)) { con_log(CL_ANN, (CE_WARN, "mrsas_add_intrs: " "avail = %d", avail)); goto mrsas_free_htable; } if (actual < count) { con_log(CL_ANN, (CE_WARN, "mrsas_add_intrs: " "Requested = %d Received = %d", count, actual)); } instance->intr_cnt = actual; /* * Get the priority of the interrupt allocated. */ if ((ret = ddi_intr_get_pri(instance->intr_htable[0], &instance->intr_pri)) != DDI_SUCCESS) { con_log(CL_ANN, (CE_WARN, "mrsas_add_intrs: " "get priority call failed")); goto mrsas_free_handles; } /* * Test for high level mutex. we don't support them. */ if (instance->intr_pri >= ddi_intr_get_hilevel_pri()) { con_log(CL_ANN, (CE_WARN, "mrsas_add_intrs: " "High level interrupts not supported.")); goto mrsas_free_handles; } con_log(CL_DLEVEL1, (CE_NOTE, "mrsas_add_intrs: intr_pri = 0x%x ", instance->intr_pri)); /* Call ddi_intr_add_handler() */ for (i = 0; i < actual; i++) { ret = ddi_intr_add_handler(instance->intr_htable[i], mrsas_isr, (caddr_t)instance, (caddr_t)(uintptr_t)i); if (ret != DDI_SUCCESS) { con_log(CL_ANN, (CE_WARN, "mrsas_add_intrs:" "failed %d", ret)); goto mrsas_free_handles; } } con_log(CL_DLEVEL1, (CE_NOTE, " ddi_intr_add_handler done")); if ((ret = ddi_intr_get_cap(instance->intr_htable[0], &instance->intr_cap)) != DDI_SUCCESS) { con_log(CL_ANN, (CE_WARN, "ddi_intr_get_cap() failed %d", ret)); goto mrsas_free_handlers; } if (instance->intr_cap & DDI_INTR_FLAG_BLOCK) { con_log(CL_ANN, (CE_WARN, "Calling ddi_intr_block _enable")); (void) ddi_intr_block_enable(instance->intr_htable, instance->intr_cnt); } else { con_log(CL_ANN, (CE_NOTE, " calling ddi_intr_enable")); for (i = 0; i < instance->intr_cnt; i++) { (void) ddi_intr_enable(instance->intr_htable[i]); con_log(CL_ANN, (CE_NOTE, "ddi intr enable returns " "%d", i)); } } return (DDI_SUCCESS); mrsas_free_handlers: for (i = 0; i < actual; i++) (void) ddi_intr_remove_handler(instance->intr_htable[i]); mrsas_free_handles: for (i = 0; i < actual; i++) (void) ddi_intr_free(instance->intr_htable[i]); mrsas_free_htable: if (instance->intr_htable != NULL) kmem_free(instance->intr_htable, instance->intr_htable_size); instance->intr_htable = NULL; instance->intr_htable_size = 0; return (DDI_FAILURE); } static void mrsas_rem_intrs(struct mrsas_instance *instance) { int i; con_log(CL_ANN, (CE_NOTE, "mrsas_rem_intrs called")); /* Disable all interrupts first */ if (instance->intr_cap & DDI_INTR_FLAG_BLOCK) { (void) ddi_intr_block_disable(instance->intr_htable, instance->intr_cnt); } else { for (i = 0; i < instance->intr_cnt; i++) { (void) ddi_intr_disable(instance->intr_htable[i]); } } /* Remove all the handlers */ for (i = 0; i < instance->intr_cnt; i++) { (void) ddi_intr_remove_handler(instance->intr_htable[i]); (void) ddi_intr_free(instance->intr_htable[i]); } if (instance->intr_htable != NULL) kmem_free(instance->intr_htable, instance->intr_htable_size); instance->intr_htable = NULL; instance->intr_htable_size = 0; } static int mrsas_tran_bus_config(dev_info_t *parent, uint_t flags, ddi_bus_config_op_t op, void *arg, dev_info_t **childp) { struct mrsas_instance *instance; int rval = NDI_SUCCESS; char *ptr = NULL; int tgt, lun; con_log(CL_ANN1, (CE_NOTE, "Bus config called for op = %x", op)); if ((instance = ddi_get_soft_state(mrsas_state, ddi_get_instance(parent))) == NULL) { return (NDI_FAILURE); } /* Hold nexus during bus_config */ ndi_devi_enter(parent); switch (op) { case BUS_CONFIG_ONE: { /* parse wwid/target name out of name given */ if ((ptr = strchr((char *)arg, '@')) == NULL) { rval = NDI_FAILURE; break; } ptr++; if (mrsas_parse_devname(arg, &tgt, &lun) != 0) { rval = NDI_FAILURE; break; } if (lun == 0) { rval = mrsas_config_ld(instance, tgt, lun, childp); } else if ((instance->tbolt || instance->skinny) && lun != 0) { rval = mrsas_tbolt_config_pd(instance, tgt, lun, childp); } else { rval = NDI_FAILURE; } break; } case BUS_CONFIG_DRIVER: case BUS_CONFIG_ALL: { rval = mrsas_config_all_devices(instance); rval = NDI_SUCCESS; break; } } if (rval == NDI_SUCCESS) { rval = ndi_busop_bus_config(parent, flags, op, arg, childp, 0); } ndi_devi_exit(parent); con_log(CL_ANN1, (CE_NOTE, "mrsas_tran_bus_config: rval = %x", rval)); return (rval); } static int mrsas_config_all_devices(struct mrsas_instance *instance) { int rval, tgt; for (tgt = 0; tgt < MRDRV_MAX_LD; tgt++) { (void) mrsas_config_ld(instance, tgt, 0, NULL); } /* Config PD devices connected to the card */ if (instance->tbolt || instance->skinny) { for (tgt = 0; tgt < instance->mr_tbolt_pd_max; tgt++) { (void) mrsas_tbolt_config_pd(instance, tgt, 1, NULL); } } rval = NDI_SUCCESS; return (rval); } static int mrsas_parse_devname(char *devnm, int *tgt, int *lun) { char devbuf[SCSI_MAXNAMELEN]; char *addr; char *p, *tp, *lp; long num; /* Parse dev name and address */ (void) strcpy(devbuf, devnm); addr = ""; for (p = devbuf; *p != '\0'; p++) { if (*p == '@') { addr = p + 1; *p = '\0'; } else if (*p == ':') { *p = '\0'; break; } } /* Parse target and lun */ for (p = tp = addr, lp = NULL; *p != '\0'; p++) { if (*p == ',') { lp = p + 1; *p = '\0'; break; } } if (tgt && tp) { if (ddi_strtol(tp, NULL, 0x10, &num)) { return (DDI_FAILURE); /* Can declare this as constant */ } *tgt = (int)num; } if (lun && lp) { if (ddi_strtol(lp, NULL, 0x10, &num)) { return (DDI_FAILURE); } *lun = (int)num; } return (DDI_SUCCESS); /* Success case */ } static int mrsas_config_ld(struct mrsas_instance *instance, uint16_t tgt, uint8_t lun, dev_info_t **ldip) { struct scsi_device *sd; dev_info_t *child; int rval; con_log(CL_DLEVEL1, (CE_NOTE, "mrsas_config_ld: t = %d l = %d", tgt, lun)); if ((child = mrsas_find_child(instance, tgt, lun)) != NULL) { if (ldip) { *ldip = child; } if (instance->mr_ld_list[tgt].flag != MRDRV_TGT_VALID) { rval = mrsas_service_evt(instance, tgt, 0, MRSAS_EVT_UNCONFIG_TGT, 0); con_log(CL_ANN1, (CE_WARN, "mr_sas: DELETING STALE ENTRY rval = %d " "tgt id = %d ", rval, tgt)); return (NDI_FAILURE); } return (NDI_SUCCESS); } sd = kmem_zalloc(sizeof (struct scsi_device), KM_SLEEP); sd->sd_address.a_hba_tran = instance->tran; sd->sd_address.a_target = (uint16_t)tgt; sd->sd_address.a_lun = (uint8_t)lun; if (scsi_hba_probe(sd, NULL) == SCSIPROBE_EXISTS) rval = mrsas_config_scsi_device(instance, sd, ldip); else rval = NDI_FAILURE; /* sd_unprobe is blank now. Free buffer manually */ if (sd->sd_inq) { kmem_free(sd->sd_inq, SUN_INQSIZE); sd->sd_inq = (struct scsi_inquiry *)NULL; } kmem_free(sd, sizeof (struct scsi_device)); con_log(CL_DLEVEL1, (CE_NOTE, "mrsas_config_ld: return rval = %d", rval)); return (rval); } int mrsas_config_scsi_device(struct mrsas_instance *instance, struct scsi_device *sd, dev_info_t **dipp) { char *nodename = NULL; char **compatible = NULL; int ncompatible = 0; char *childname; dev_info_t *ldip = NULL; int tgt = sd->sd_address.a_target; int lun = sd->sd_address.a_lun; int dtype = sd->sd_inq->inq_dtype & DTYPE_MASK; int rval; con_log(CL_DLEVEL1, (CE_NOTE, "mr_sas: scsi_device t%dL%d", tgt, lun)); scsi_hba_nodename_compatible_get(sd->sd_inq, NULL, dtype, NULL, &nodename, &compatible, &ncompatible); if (nodename == NULL) { con_log(CL_ANN1, (CE_WARN, "mr_sas: Found no compatible driver " "for t%dL%d", tgt, lun)); rval = NDI_FAILURE; goto finish; } childname = (dtype == DTYPE_DIRECT) ? "sd" : nodename; con_log(CL_DLEVEL1, (CE_NOTE, "mr_sas: Childname = %2s nodename = %s", childname, nodename)); /* Create a dev node */ rval = ndi_devi_alloc(instance->dip, childname, DEVI_SID_NODEID, &ldip); con_log(CL_DLEVEL1, (CE_NOTE, "mr_sas_config_scsi_device: ndi_devi_alloc rval = %x", rval)); if (rval == NDI_SUCCESS) { if (ndi_prop_update_int(DDI_DEV_T_NONE, ldip, "target", tgt) != DDI_PROP_SUCCESS) { con_log(CL_ANN1, (CE_WARN, "mr_sas: unable to create " "property for t%dl%d target", tgt, lun)); rval = NDI_FAILURE; goto finish; } if (ndi_prop_update_int(DDI_DEV_T_NONE, ldip, "lun", lun) != DDI_PROP_SUCCESS) { con_log(CL_ANN1, (CE_WARN, "mr_sas: unable to create " "property for t%dl%d lun", tgt, lun)); rval = NDI_FAILURE; goto finish; } if (ndi_prop_update_string_array(DDI_DEV_T_NONE, ldip, "compatible", compatible, ncompatible) != DDI_PROP_SUCCESS) { con_log(CL_ANN1, (CE_WARN, "mr_sas: unable to create " "property for t%dl%d compatible", tgt, lun)); rval = NDI_FAILURE; goto finish; } rval = ndi_devi_online(ldip, NDI_ONLINE_ATTACH); if (rval != NDI_SUCCESS) { con_log(CL_ANN1, (CE_WARN, "mr_sas: unable to online " "t%dl%d", tgt, lun)); ndi_prop_remove_all(ldip); (void) ndi_devi_free(ldip); } else { con_log(CL_ANN1, (CE_CONT, "mr_sas: online Done :" "0 t%dl%d", tgt, lun)); } } finish: if (dipp) { *dipp = ldip; } con_log(CL_DLEVEL1, (CE_NOTE, "mr_sas: config_scsi_device rval = %d t%dL%d", rval, tgt, lun)); scsi_hba_nodename_compatible_free(nodename, compatible); return (rval); } /*ARGSUSED*/ int mrsas_service_evt(struct mrsas_instance *instance, int tgt, int lun, int event, uint64_t wwn) { struct mrsas_eventinfo *mrevt = NULL; con_log(CL_ANN1, (CE_NOTE, "mrsas_service_evt called for t%dl%d event = %d", tgt, lun, event)); if ((instance->taskq == NULL) || (mrevt = kmem_zalloc(sizeof (struct mrsas_eventinfo), KM_NOSLEEP)) == NULL) { return (ENOMEM); } mrevt->instance = instance; mrevt->tgt = tgt; mrevt->lun = lun; mrevt->event = event; mrevt->wwn = wwn; if ((ddi_taskq_dispatch(instance->taskq, (void (*)(void *))mrsas_issue_evt_taskq, mrevt, DDI_NOSLEEP)) != DDI_SUCCESS) { con_log(CL_ANN1, (CE_NOTE, "mr_sas: Event task failed for t%dl%d event = %d", tgt, lun, event)); kmem_free(mrevt, sizeof (struct mrsas_eventinfo)); return (DDI_FAILURE); } DTRACE_PROBE3(service_evt, int, tgt, int, lun, int, event); return (DDI_SUCCESS); } static void mrsas_issue_evt_taskq(struct mrsas_eventinfo *mrevt) { struct mrsas_instance *instance = mrevt->instance; dev_info_t *dip, *pdip; char *devname; con_log(CL_ANN1, (CE_NOTE, "mrsas_issue_evt_taskq: called for" " tgt %d lun %d event %d", mrevt->tgt, mrevt->lun, mrevt->event)); if (mrevt->tgt < MRDRV_MAX_LD && mrevt->lun == 0) { mutex_enter(&instance->config_dev_mtx); dip = instance->mr_ld_list[mrevt->tgt].dip; mutex_exit(&instance->config_dev_mtx); } else { mutex_enter(&instance->config_dev_mtx); dip = instance->mr_tbolt_pd_list[mrevt->tgt].dip; mutex_exit(&instance->config_dev_mtx); } ndi_devi_enter(instance->dip); switch (mrevt->event) { case MRSAS_EVT_CONFIG_TGT: if (dip == NULL) { if (mrevt->lun == 0) { (void) mrsas_config_ld(instance, mrevt->tgt, 0, NULL); } else if (instance->tbolt || instance->skinny) { (void) mrsas_tbolt_config_pd(instance, mrevt->tgt, 1, NULL); } con_log(CL_ANN1, (CE_NOTE, "mr_sas: EVT_CONFIG_TGT called:" " for tgt %d lun %d event %d", mrevt->tgt, mrevt->lun, mrevt->event)); } else { con_log(CL_ANN1, (CE_NOTE, "mr_sas: EVT_CONFIG_TGT dip != NULL:" " for tgt %d lun %d event %d", mrevt->tgt, mrevt->lun, mrevt->event)); } break; case MRSAS_EVT_UNCONFIG_TGT: if (dip) { if (i_ddi_devi_attached(dip)) { pdip = ddi_get_parent(dip); devname = kmem_zalloc(MAXNAMELEN + 1, KM_SLEEP); (void) ddi_deviname(dip, devname); (void) devfs_clean(pdip, devname + 1, DV_CLEAN_FORCE); kmem_free(devname, MAXNAMELEN + 1); } (void) ndi_devi_offline(dip, NDI_DEVI_REMOVE); con_log(CL_ANN1, (CE_NOTE, "mr_sas: EVT_UNCONFIG_TGT called:" " for tgt %d lun %d event %d", mrevt->tgt, mrevt->lun, mrevt->event)); } else { con_log(CL_ANN1, (CE_NOTE, "mr_sas: EVT_UNCONFIG_TGT dip == NULL:" " for tgt %d lun %d event %d", mrevt->tgt, mrevt->lun, mrevt->event)); } break; } kmem_free(mrevt, sizeof (struct mrsas_eventinfo)); ndi_devi_exit(instance->dip); } int mrsas_mode_sense_build(struct scsi_pkt *pkt) { union scsi_cdb *cdbp; uint16_t page_code; struct scsa_cmd *acmd; struct buf *bp; struct mode_header *modehdrp; cdbp = (void *)pkt->pkt_cdbp; page_code = cdbp->cdb_un.sg.scsi[0]; acmd = PKT2CMD(pkt); bp = acmd->cmd_buf; if ((!bp) && bp->b_un.b_addr && bp->b_bcount && acmd->cmd_dmacount) { con_log(CL_ANN1, (CE_WARN, "Failing MODESENSE Command")); /* ADD pkt statistics as Command failed. */ return (0); } bp_mapin(bp); bzero(bp->b_un.b_addr, bp->b_bcount); switch (page_code) { case 0x3: { struct mode_format *page3p = NULL; modehdrp = (struct mode_header *)(bp->b_un.b_addr); modehdrp->bdesc_length = MODE_BLK_DESC_LENGTH; page3p = (void *)((caddr_t)modehdrp + MODE_HEADER_LENGTH + MODE_BLK_DESC_LENGTH); page3p->mode_page.code = 0x3; page3p->mode_page.length = (uchar_t)(sizeof (struct mode_format)); page3p->data_bytes_sect = 512; page3p->sect_track = 63; break; } case 0x4: { struct mode_geometry *page4p = NULL; modehdrp = (struct mode_header *)(bp->b_un.b_addr); modehdrp->bdesc_length = MODE_BLK_DESC_LENGTH; page4p = (void *)((caddr_t)modehdrp + MODE_HEADER_LENGTH + MODE_BLK_DESC_LENGTH); page4p->mode_page.code = 0x4; page4p->mode_page.length = (uchar_t)(sizeof (struct mode_geometry)); page4p->heads = 255; page4p->rpm = 10000; break; } default: break; } return (0); }