diff options
Diffstat (limited to 'drivers/scsi/megaraid/megaraid_sas_base.c')
-rw-r--r-- | drivers/scsi/megaraid/megaraid_sas_base.c | 275 |
1 files changed, 213 insertions, 62 deletions
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 5eac650..e45c865 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -18,7 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * FILE: megaraid_sas_base.c - * Version : v00.00.05.38-rc1 + * Version : v00.00.06.12-rc1 * * Authors: LSI Corporation * Sreenivas Bagalkote @@ -54,6 +54,7 @@ #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> +#include <scsi/scsi_tcq.h> #include "megaraid_sas_fusion.h" #include "megaraid_sas.h" @@ -83,7 +84,7 @@ MODULE_VERSION(MEGASAS_VERSION); MODULE_AUTHOR("megaraidlinux@lsi.com"); MODULE_DESCRIPTION("LSI MegaRAID SAS Driver"); -int megasas_transition_to_ready(struct megasas_instance *instance); +int megasas_transition_to_ready(struct megasas_instance *instance, int ocr); static int megasas_get_pd_list(struct megasas_instance *instance); static int megasas_issue_init_mfi(struct megasas_instance *instance); static int megasas_register_aen(struct megasas_instance *instance, @@ -113,6 +114,8 @@ static struct pci_device_id megasas_pci_table[] = { /* xscale IOP */ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FUSION)}, /* Fusion */ + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_INVADER)}, + /* Invader */ {} }; @@ -212,6 +215,10 @@ megasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd) cmd->scmd = NULL; cmd->frame_count = 0; + if ((instance->pdev->device != PCI_DEVICE_ID_LSI_FUSION) && + (instance->pdev->device != PCI_DEVICE_ID_LSI_INVADER) && + (reset_devices)) + cmd->frame->hdr.cmd = MFI_CMD_INVALID; list_add_tail(&cmd->list, &instance->cmd_pool); spin_unlock_irqrestore(&instance->cmd_pool_lock, flags); @@ -1582,7 +1589,8 @@ void megaraid_sas_kill_hba(struct megasas_instance *instance) { if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION)) { + (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER)) { writel(MFI_STOP_ADP, &instance->reg_set->doorbell); } else { writel(MFI_STOP_ADP, &instance->reg_set->inbound_doorbell); @@ -1955,7 +1963,8 @@ static int megasas_reset_bus_host(struct scsi_cmnd *scmd) /* * First wait for all commands to complete */ - if (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) + if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER)) ret = megasas_reset_fusion(scmd->device->host); else ret = megasas_generic_reset(scmd); @@ -2056,6 +2065,20 @@ megasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd) } } +static int megasas_change_queue_depth(struct scsi_device *sdev, + int queue_depth, int reason) +{ + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + + if (queue_depth > sdev->host->can_queue) + queue_depth = sdev->host->can_queue; + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), + queue_depth); + + return queue_depth; +} + /* * Scsi host template for megaraid_sas driver */ @@ -2073,6 +2096,7 @@ static struct scsi_host_template megasas_template = { .eh_timed_out = megasas_reset_timer, .bios_param = megasas_bios_param, .use_clustering = ENABLE_CLUSTERING, + .change_queue_depth = megasas_change_queue_depth, }; /** @@ -2144,7 +2168,16 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, cmd->scmd->SCp.ptr = NULL; switch (hdr->cmd) { - + case MFI_CMD_INVALID: + /* Some older 1068 controller FW may keep a pended + MR_DCMD_CTRL_EVENT_GET_INFO left over from the main kernel + when booting the kdump kernel. Ignore this command to + prevent a kernel panic on shutdown of the kdump kernel. */ + printk(KERN_WARNING "megaraid_sas: MFI_CMD_INVALID command " + "completed.\n"); + printk(KERN_WARNING "megaraid_sas: If you have a controller " + "other than PERC5, please upgrade your firmware.\n"); + break; case MFI_CMD_PD_SCSI_IO: case MFI_CMD_LD_SCSI_IO: @@ -2460,7 +2493,7 @@ process_fw_state_change_wq(struct work_struct *work) msleep(1000); } - if (megasas_transition_to_ready(instance)) { + if (megasas_transition_to_ready(instance, 1)) { printk(KERN_NOTICE "megaraid_sas:adapter not ready\n"); megaraid_sas_kill_hba(instance); @@ -2515,7 +2548,7 @@ megasas_deplete_reply_queue(struct megasas_instance *instance, instance->reg_set) ) == 0) { /* Hardware may not set outbound_intr_status in MSI-X mode */ - if (!instance->msi_flag) + if (!instance->msix_vectors) return IRQ_NONE; } @@ -2573,16 +2606,14 @@ megasas_deplete_reply_queue(struct megasas_instance *instance, */ static irqreturn_t megasas_isr(int irq, void *devp) { - struct megasas_instance *instance; + struct megasas_irq_context *irq_context = devp; + struct megasas_instance *instance = irq_context->instance; unsigned long flags; irqreturn_t rc; - if (atomic_read( - &(((struct megasas_instance *)devp)->fw_reset_no_pci_access))) + if (atomic_read(&instance->fw_reset_no_pci_access)) return IRQ_HANDLED; - instance = (struct megasas_instance *)devp; - spin_lock_irqsave(&instance->hba_lock, flags); rc = megasas_deplete_reply_queue(instance, DID_OK); spin_unlock_irqrestore(&instance->hba_lock, flags); @@ -2600,7 +2631,7 @@ static irqreturn_t megasas_isr(int irq, void *devp) * has to wait for the ready state. */ int -megasas_transition_to_ready(struct megasas_instance* instance) +megasas_transition_to_ready(struct megasas_instance *instance, int ocr) { int i; u8 max_wait; @@ -2622,11 +2653,13 @@ megasas_transition_to_ready(struct megasas_instance* instance) switch (fw_state) { case MFI_STATE_FAULT: - printk(KERN_DEBUG "megasas: FW in FAULT state!!\n"); - max_wait = MEGASAS_RESET_WAIT_TIME; - cur_state = MFI_STATE_FAULT; - break; + if (ocr) { + max_wait = MEGASAS_RESET_WAIT_TIME; + cur_state = MFI_STATE_FAULT; + break; + } else + return -ENODEV; case MFI_STATE_WAIT_HANDSHAKE: /* @@ -2637,7 +2670,9 @@ megasas_transition_to_ready(struct megasas_instance* instance) (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) || (instance->pdev->device == - PCI_DEVICE_ID_LSI_FUSION)) { + PCI_DEVICE_ID_LSI_FUSION) || + (instance->pdev->device == + PCI_DEVICE_ID_LSI_INVADER)) { writel( MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG, &instance->reg_set->doorbell); @@ -2657,7 +2692,9 @@ megasas_transition_to_ready(struct megasas_instance* instance) (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) || (instance->pdev->device == - PCI_DEVICE_ID_LSI_FUSION)) { + PCI_DEVICE_ID_LSI_FUSION) || + (instance->pdev->device == + PCI_DEVICE_ID_LSI_INVADER)) { writel(MFI_INIT_HOTPLUG, &instance->reg_set->doorbell); } else @@ -2678,11 +2715,15 @@ megasas_transition_to_ready(struct megasas_instance* instance) (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) || (instance->pdev->device - == PCI_DEVICE_ID_LSI_FUSION)) { + == PCI_DEVICE_ID_LSI_FUSION) || + (instance->pdev->device + == PCI_DEVICE_ID_LSI_INVADER)) { writel(MFI_RESET_FLAGS, &instance->reg_set->doorbell); - if (instance->pdev->device == - PCI_DEVICE_ID_LSI_FUSION) { + if ((instance->pdev->device == + PCI_DEVICE_ID_LSI_FUSION) || + (instance->pdev->device == + PCI_DEVICE_ID_LSI_INVADER)) { for (i = 0; i < (10 * 1000); i += 20) { if (readl( &instance-> @@ -2905,6 +2946,10 @@ static int megasas_create_frame_pool(struct megasas_instance *instance) memset(cmd->frame, 0, total_sz); cmd->frame->io.context = cmd->index; cmd->frame->io.pad_0 = 0; + if ((instance->pdev->device != PCI_DEVICE_ID_LSI_FUSION) && + (instance->pdev->device != PCI_DEVICE_ID_LSI_INVADER) && + (reset_devices)) + cmd->frame->hdr.cmd = MFI_CMD_INVALID; } return 0; @@ -3454,21 +3499,23 @@ static int megasas_init_fw(struct megasas_instance *instance) u32 max_sectors_1; u32 max_sectors_2; u32 tmp_sectors, msix_enable; + resource_size_t base_addr; struct megasas_register_set __iomem *reg_set; struct megasas_ctrl_info *ctrl_info; unsigned long bar_list; + int i; /* Find first memory bar */ bar_list = pci_select_bars(instance->pdev, IORESOURCE_MEM); instance->bar = find_first_bit(&bar_list, sizeof(unsigned long)); - instance->base_addr = pci_resource_start(instance->pdev, instance->bar); if (pci_request_selected_regions(instance->pdev, instance->bar, "megasas: LSI")) { printk(KERN_DEBUG "megasas: IO memory region busy!\n"); return -EBUSY; } - instance->reg_set = ioremap_nocache(instance->base_addr, 8192); + base_addr = pci_resource_start(instance->pdev, instance->bar); + instance->reg_set = ioremap_nocache(base_addr, 8192); if (!instance->reg_set) { printk(KERN_DEBUG "megasas: Failed to map IO mem\n"); @@ -3479,6 +3526,7 @@ static int megasas_init_fw(struct megasas_instance *instance) switch (instance->pdev->device) { case PCI_DEVICE_ID_LSI_FUSION: + case PCI_DEVICE_ID_LSI_INVADER: instance->instancet = &megasas_instance_template_fusion; break; case PCI_DEVICE_ID_LSI_SAS1078R: @@ -3500,18 +3548,52 @@ static int megasas_init_fw(struct megasas_instance *instance) break; } - /* - * We expect the FW state to be READY - */ - if (megasas_transition_to_ready(instance)) - goto fail_ready_state; + if (megasas_transition_to_ready(instance, 0)) { + atomic_set(&instance->fw_reset_no_pci_access, 1); + instance->instancet->adp_reset + (instance, instance->reg_set); + atomic_set(&instance->fw_reset_no_pci_access, 0); + dev_info(&instance->pdev->dev, + "megasas: FW restarted successfully from %s!\n", + __func__); + + /*waitting for about 30 second before retry*/ + ssleep(30); + + if (megasas_transition_to_ready(instance, 0)) + goto fail_ready_state; + } /* Check if MSI-X is supported while in ready state */ msix_enable = (instance->instancet->read_fw_status_reg(reg_set) & 0x4000000) >> 0x1a; - if (msix_enable && !msix_disable && - !pci_enable_msix(instance->pdev, &instance->msixentry, 1)) - instance->msi_flag = 1; + if (msix_enable && !msix_disable) { + /* Check max MSI-X vectors */ + if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER)) { + instance->msix_vectors = (readl(&instance->reg_set-> + outbound_scratch_pad_2 + ) & 0x1F) + 1; + } else + instance->msix_vectors = 1; + /* Don't bother allocating more MSI-X vectors than cpus */ + instance->msix_vectors = min(instance->msix_vectors, + (unsigned int)num_online_cpus()); + for (i = 0; i < instance->msix_vectors; i++) + instance->msixentry[i].entry = i; + i = pci_enable_msix(instance->pdev, instance->msixentry, + instance->msix_vectors); + if (i >= 0) { + if (i) { + if (!pci_enable_msix(instance->pdev, + instance->msixentry, i)) + instance->msix_vectors = i; + else + instance->msix_vectors = 0; + } + } else + instance->msix_vectors = 0; + } /* Get operational params, sge flags, send init cmd to controller */ if (instance->instancet->init_adapter(instance)) @@ -3875,7 +3957,8 @@ static int megasas_io_attach(struct megasas_instance *instance) host->max_cmd_len = 16; /* Fusion only supports host reset */ - if (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) { + if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER)) { host->hostt->eh_device_reset_handler = NULL; host->hostt->eh_bus_reset_handler = NULL; } @@ -3925,7 +4008,7 @@ fail_set_dma_mask: static int __devinit megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) { - int rval, pos; + int rval, pos, i, j; struct Scsi_Host *host; struct megasas_instance *instance; u16 control = 0; @@ -3985,6 +4068,7 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) switch (instance->pdev->device) { case PCI_DEVICE_ID_LSI_FUSION: + case PCI_DEVICE_ID_LSI_INVADER: { struct fusion_context *fusion; @@ -4076,7 +4160,8 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) instance->last_time = 0; instance->disableOnlineCtrlReset = 1; - if (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) + if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER)) INIT_WORK(&instance->work_init, megasas_fusion_ocr_wq); else INIT_WORK(&instance->work_init, process_fw_state_change_wq); @@ -4090,11 +4175,32 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) /* * Register IRQ */ - if (request_irq(instance->msi_flag ? instance->msixentry.vector : - pdev->irq, instance->instancet->service_isr, - IRQF_SHARED, "megasas", instance)) { - printk(KERN_DEBUG "megasas: Failed to register IRQ\n"); - goto fail_irq; + if (instance->msix_vectors) { + for (i = 0 ; i < instance->msix_vectors; i++) { + instance->irq_context[i].instance = instance; + instance->irq_context[i].MSIxIndex = i; + if (request_irq(instance->msixentry[i].vector, + instance->instancet->service_isr, 0, + "megasas", + &instance->irq_context[i])) { + printk(KERN_DEBUG "megasas: Failed to " + "register IRQ for vector %d.\n", i); + for (j = 0 ; j < i ; j++) + free_irq( + instance->msixentry[j].vector, + &instance->irq_context[j]); + goto fail_irq; + } + } + } else { + instance->irq_context[0].instance = instance; + instance->irq_context[0].MSIxIndex = 0; + if (request_irq(pdev->irq, instance->instancet->service_isr, + IRQF_SHARED, "megasas", + &instance->irq_context[0])) { + printk(KERN_DEBUG "megasas: Failed to register IRQ\n"); + goto fail_irq; + } } instance->instancet->enable_intr(instance->reg_set); @@ -4138,15 +4244,20 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_drvdata(pdev, NULL); instance->instancet->disable_intr(instance->reg_set); - free_irq(instance->msi_flag ? instance->msixentry.vector : - instance->pdev->irq, instance); + if (instance->msix_vectors) + for (i = 0 ; i < instance->msix_vectors; i++) + free_irq(instance->msixentry[i].vector, + &instance->irq_context[i]); + else + free_irq(instance->pdev->irq, &instance->irq_context[0]); fail_irq: - if (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) + if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER)) megasas_release_fusion(instance); else megasas_release_mfi(instance); fail_init_mfi: - if (instance->msi_flag) + if (instance->msix_vectors) pci_disable_msix(instance->pdev); fail_alloc_dma_buf: if (instance->evt_detail) @@ -4262,6 +4373,7 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state) { struct Scsi_Host *host; struct megasas_instance *instance; + int i; instance = pci_get_drvdata(pdev); host = instance->host; @@ -4285,9 +4397,14 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state) pci_set_drvdata(instance->pdev, instance); instance->instancet->disable_intr(instance->reg_set); - free_irq(instance->msi_flag ? instance->msixentry.vector : - instance->pdev->irq, instance); - if (instance->msi_flag) + + if (instance->msix_vectors) + for (i = 0 ; i < instance->msix_vectors; i++) + free_irq(instance->msixentry[i].vector, + &instance->irq_context[i]); + else + free_irq(instance->pdev->irq, &instance->irq_context[0]); + if (instance->msix_vectors) pci_disable_msix(instance->pdev); pci_save_state(pdev); @@ -4305,7 +4422,7 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state) static int megasas_resume(struct pci_dev *pdev) { - int rval; + int rval, i, j; struct Scsi_Host *host; struct megasas_instance *instance; @@ -4339,15 +4456,17 @@ megasas_resume(struct pci_dev *pdev) /* * We expect the FW state to be READY */ - if (megasas_transition_to_ready(instance)) + if (megasas_transition_to_ready(instance, 0)) goto fail_ready_state; /* Now re-enable MSI-X */ - if (instance->msi_flag) - pci_enable_msix(instance->pdev, &instance->msixentry, 1); + if (instance->msix_vectors) + pci_enable_msix(instance->pdev, instance->msixentry, + instance->msix_vectors); switch (instance->pdev->device) { case PCI_DEVICE_ID_LSI_FUSION: + case PCI_DEVICE_ID_LSI_INVADER: { megasas_reset_reply_desc(instance); if (megasas_ioc_init_fusion(instance)) { @@ -4373,11 +4492,32 @@ megasas_resume(struct pci_dev *pdev) /* * Register IRQ */ - if (request_irq(instance->msi_flag ? instance->msixentry.vector : - pdev->irq, instance->instancet->service_isr, - IRQF_SHARED, "megasas", instance)) { - printk(KERN_ERR "megasas: Failed to register IRQ\n"); - goto fail_irq; + if (instance->msix_vectors) { + for (i = 0 ; i < instance->msix_vectors; i++) { + instance->irq_context[i].instance = instance; + instance->irq_context[i].MSIxIndex = i; + if (request_irq(instance->msixentry[i].vector, + instance->instancet->service_isr, 0, + "megasas", + &instance->irq_context[i])) { + printk(KERN_DEBUG "megasas: Failed to " + "register IRQ for vector %d.\n", i); + for (j = 0 ; j < i ; j++) + free_irq( + instance->msixentry[j].vector, + &instance->irq_context[j]); + goto fail_irq; + } + } + } else { + instance->irq_context[0].instance = instance; + instance->irq_context[0].MSIxIndex = 0; + if (request_irq(pdev->irq, instance->instancet->service_isr, + IRQF_SHARED, "megasas", + &instance->irq_context[0])) { + printk(KERN_DEBUG "megasas: Failed to register IRQ\n"); + goto fail_irq; + } } instance->instancet->enable_intr(instance->reg_set); @@ -4474,13 +4614,18 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev) instance->instancet->disable_intr(instance->reg_set); - free_irq(instance->msi_flag ? instance->msixentry.vector : - instance->pdev->irq, instance); - if (instance->msi_flag) + if (instance->msix_vectors) + for (i = 0 ; i < instance->msix_vectors; i++) + free_irq(instance->msixentry[i].vector, + &instance->irq_context[i]); + else + free_irq(instance->pdev->irq, &instance->irq_context[0]); + if (instance->msix_vectors) pci_disable_msix(instance->pdev); switch (instance->pdev->device) { case PCI_DEVICE_ID_LSI_FUSION: + case PCI_DEVICE_ID_LSI_INVADER: megasas_release_fusion(instance); for (i = 0; i < 2 ; i++) if (fusion->ld_map[i]) @@ -4521,14 +4666,20 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev) */ static void megasas_shutdown(struct pci_dev *pdev) { + int i; struct megasas_instance *instance = pci_get_drvdata(pdev); + instance->unload = 1; megasas_flush_cache(instance); megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN); instance->instancet->disable_intr(instance->reg_set); - free_irq(instance->msi_flag ? instance->msixentry.vector : - instance->pdev->irq, instance); - if (instance->msi_flag) + if (instance->msix_vectors) + for (i = 0 ; i < instance->msix_vectors; i++) + free_irq(instance->msixentry[i].vector, + &instance->irq_context[i]); + else + free_irq(instance->pdev->irq, &instance->irq_context[0]); + if (instance->msix_vectors) pci_disable_msix(instance->pdev); } |