Merge branch 'stable/cleanups-3.2' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / drivers / scsi / megaraid / megaraid_sas_base.c
index 776d019..29a994f 100644 (file)
@@ -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.40-rc1
+ *  Version : v00.00.06.12-rc1
  *
  *  Authors: LSI Corporation
  *           Sreenivas Bagalkote
@@ -84,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,
@@ -114,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 */
        {}
 };
 
@@ -213,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);
@@ -1583,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);
@@ -1907,7 +1914,6 @@ static int megasas_generic_reset(struct scsi_cmnd *scmd)
 static enum
 blk_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd)
 {
-       struct megasas_cmd *cmd = (struct megasas_cmd *)scmd->SCp.ptr;
        struct megasas_instance *instance;
        unsigned long flags;
 
@@ -1916,7 +1922,7 @@ blk_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd)
                return BLK_EH_NOT_HANDLED;
        }
 
-       instance = cmd->instance;
+       instance = (struct megasas_instance *)scmd->device->host->hostdata;
        if (!(instance->flag & MEGASAS_FW_BUSY)) {
                /* FW is busy, throttle IO */
                spin_lock_irqsave(instance->host->host_lock, flags);
@@ -1957,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);
@@ -2161,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:
 
@@ -2477,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);
@@ -2532,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;
        }
 
@@ -2590,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);
@@ -2617,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;
@@ -2639,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:
                        /*
@@ -2654,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);
@@ -2674,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
@@ -2695,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->
@@ -2922,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;
@@ -3474,6 +3502,7 @@ static int megasas_init_fw(struct megasas_instance *instance)
        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);
@@ -3496,6 +3525,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:
@@ -3520,15 +3550,39 @@ static int megasas_init_fw(struct megasas_instance *instance)
        /*
         * 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;
 
        /* 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))
@@ -3892,7 +3946,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;
        }
@@ -3942,7 +3997,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;
@@ -4002,6 +4057,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;
 
@@ -4094,7 +4150,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);
@@ -4108,11 +4165,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);
@@ -4156,15 +4234,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)
@@ -4280,6 +4363,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;
@@ -4303,9 +4387,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);
@@ -4323,7 +4412,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;
 
@@ -4357,15 +4446,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)) {
@@ -4391,11 +4482,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);
@@ -4492,13 +4604,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])
@@ -4539,14 +4656,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);
 }