]> err.no Git - linux-2.6/blobdiff - drivers/scsi/ipr.c
[SCSI] ipr: Use PCI-E reset API for new ipr adapter
[linux-2.6] / drivers / scsi / ipr.c
index d9f208343a24cb5124a5ba304603630ca5727033..4baa79e686794ba5915717c2998860b6031fd91b 100644 (file)
@@ -92,6 +92,7 @@ static unsigned int ipr_fastfail = 0;
 static unsigned int ipr_transop_timeout = 0;
 static unsigned int ipr_enable_cache = 1;
 static unsigned int ipr_debug = 0;
+static unsigned int ipr_dual_ioa_raid = 1;
 static DEFINE_SPINLOCK(ipr_driver_lock);
 
 /* This table describes the differences between DMA controller chips */
@@ -158,6 +159,8 @@ module_param_named(enable_cache, ipr_enable_cache, int, 0);
 MODULE_PARM_DESC(enable_cache, "Enable adapter's non-volatile write cache (default: 1)");
 module_param_named(debug, ipr_debug, int, 0);
 MODULE_PARM_DESC(debug, "Enable device driver debugging logging. Set to 1 to enable. (default: 0)");
+module_param_named(dual_ioa_raid, ipr_dual_ioa_raid, int, 0);
+MODULE_PARM_DESC(dual_ioa_raid, "Enable dual adapter RAID support. Set to 1 to enable. (default: 1)");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(IPR_DRIVER_VERSION);
 
@@ -5881,6 +5884,94 @@ static int ipr_ioafp_mode_sense_page28(struct ipr_cmnd *ipr_cmd)
        return IPR_RC_JOB_RETURN;
 }
 
+/**
+ * ipr_ioafp_mode_select_page24 - Issue Mode Select to IOA
+ * @ipr_cmd:   ipr command struct
+ *
+ * This function enables dual IOA RAID support if possible.
+ *
+ * Return value:
+ *     IPR_RC_JOB_RETURN
+ **/
+static int ipr_ioafp_mode_select_page24(struct ipr_cmnd *ipr_cmd)
+{
+       struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+       struct ipr_mode_pages *mode_pages = &ioa_cfg->vpd_cbs->mode_pages;
+       struct ipr_mode_page24 *mode_page;
+       int length;
+
+       ENTER;
+       mode_page = ipr_get_mode_page(mode_pages, 0x24,
+                                     sizeof(struct ipr_mode_page24));
+
+       if (mode_page)
+               mode_page->flags |= IPR_ENABLE_DUAL_IOA_AF;
+
+       length = mode_pages->hdr.length + 1;
+       mode_pages->hdr.length = 0;
+
+       ipr_build_mode_select(ipr_cmd, cpu_to_be32(IPR_IOA_RES_HANDLE), 0x11,
+                             ioa_cfg->vpd_cbs_dma + offsetof(struct ipr_misc_cbs, mode_pages),
+                             length);
+
+       ipr_cmd->job_step = ipr_ioafp_mode_sense_page28;
+       ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, IPR_INTERNAL_TIMEOUT);
+
+       LEAVE;
+       return IPR_RC_JOB_RETURN;
+}
+
+/**
+ * ipr_reset_mode_sense_page24_failed - Handle failure of IOAFP mode sense
+ * @ipr_cmd:   ipr command struct
+ *
+ * This function handles the failure of a Mode Sense to the IOAFP.
+ * Some adapters do not handle all mode pages.
+ *
+ * Return value:
+ *     IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
+ **/
+static int ipr_reset_mode_sense_page24_failed(struct ipr_cmnd *ipr_cmd)
+{
+       u32 ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc);
+
+       if (ioasc == IPR_IOASC_IR_INVALID_REQ_TYPE_OR_PKT) {
+               ipr_cmd->job_step = ipr_ioafp_mode_sense_page28;
+               return IPR_RC_JOB_CONTINUE;
+       }
+
+       return ipr_reset_cmd_failed(ipr_cmd);
+}
+
+/**
+ * ipr_ioafp_mode_sense_page24 - Issue Page 24 Mode Sense to IOA
+ * @ipr_cmd:   ipr command struct
+ *
+ * This function send a mode sense to the IOA to retrieve
+ * the IOA Advanced Function Control mode page.
+ *
+ * Return value:
+ *     IPR_RC_JOB_RETURN
+ **/
+static int ipr_ioafp_mode_sense_page24(struct ipr_cmnd *ipr_cmd)
+{
+       struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+
+       ENTER;
+       ipr_build_mode_sense(ipr_cmd, cpu_to_be32(IPR_IOA_RES_HANDLE),
+                            0x24, ioa_cfg->vpd_cbs_dma +
+                            offsetof(struct ipr_misc_cbs, mode_pages),
+                            sizeof(struct ipr_mode_pages));
+
+       ipr_cmd->job_step = ipr_ioafp_mode_select_page24;
+       ipr_cmd->job_step_failed = ipr_reset_mode_sense_page24_failed;
+
+       ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, IPR_INTERNAL_TIMEOUT);
+
+       LEAVE;
+       return IPR_RC_JOB_RETURN;
+}
+
 /**
  * ipr_init_res_table - Initialize the resource table
  * @ipr_cmd:   ipr command struct
@@ -5949,7 +6040,10 @@ static int ipr_init_res_table(struct ipr_cmnd *ipr_cmd)
                }
        }
 
-       ipr_cmd->job_step = ipr_ioafp_mode_sense_page28;
+       if (ioa_cfg->dual_raid && ipr_dual_ioa_raid)
+               ipr_cmd->job_step = ipr_ioafp_mode_sense_page24;
+       else
+               ipr_cmd->job_step = ipr_ioafp_mode_sense_page28;
 
        LEAVE;
        return IPR_RC_JOB_CONTINUE;
@@ -5971,8 +6065,11 @@ static int ipr_ioafp_query_ioa_cfg(struct ipr_cmnd *ipr_cmd)
        struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb;
        struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl;
        struct ipr_inquiry_page3 *ucode_vpd = &ioa_cfg->vpd_cbs->page3_data;
+       struct ipr_inquiry_cap *cap = &ioa_cfg->vpd_cbs->cap;
 
        ENTER;
+       if (cap->cap & IPR_CAP_DUAL_IOA_RAID)
+               ioa_cfg->dual_raid = 1;
        dev_info(&ioa_cfg->pdev->dev, "Adapter firmware version: %02X%02X%02X%02X\n",
                 ucode_vpd->major_release, ucode_vpd->card_type,
                 ucode_vpd->minor_release[0], ucode_vpd->minor_release[1]);
@@ -6055,6 +6152,37 @@ static int ipr_inquiry_page_supported(struct ipr_inquiry_page0 *page0, u8 page)
        return 0;
 }
 
+/**
+ * ipr_ioafp_cap_inquiry - Send a Page 0xD0 Inquiry to the adapter.
+ * @ipr_cmd:   ipr command struct
+ *
+ * This function sends a Page 0xD0 inquiry to the adapter
+ * to retrieve adapter capabilities.
+ *
+ * Return value:
+ *     IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN
+ **/
+static int ipr_ioafp_cap_inquiry(struct ipr_cmnd *ipr_cmd)
+{
+       struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+       struct ipr_inquiry_page0 *page0 = &ioa_cfg->vpd_cbs->page0_data;
+       struct ipr_inquiry_cap *cap = &ioa_cfg->vpd_cbs->cap;
+
+       ENTER;
+       ipr_cmd->job_step = ipr_ioafp_query_ioa_cfg;
+       memset(cap, 0, sizeof(*cap));
+
+       if (ipr_inquiry_page_supported(page0, 0xD0)) {
+               ipr_ioafp_inquiry(ipr_cmd, 1, 0xD0,
+                                 ioa_cfg->vpd_cbs_dma + offsetof(struct ipr_misc_cbs, cap),
+                                 sizeof(struct ipr_inquiry_cap));
+               return IPR_RC_JOB_RETURN;
+       }
+
+       LEAVE;
+       return IPR_RC_JOB_CONTINUE;
+}
+
 /**
  * ipr_ioafp_page3_inquiry - Send a Page 3 Inquiry to the adapter.
  * @ipr_cmd:   ipr command struct
@@ -6075,7 +6203,7 @@ static int ipr_ioafp_page3_inquiry(struct ipr_cmnd *ipr_cmd)
        if (!ipr_inquiry_page_supported(page0, 1))
                ioa_cfg->cache_state = CACHE_NONE;
 
-       ipr_cmd->job_step = ipr_ioafp_query_ioa_cfg;
+       ipr_cmd->job_step = ipr_ioafp_cap_inquiry;
 
        ipr_ioafp_inquiry(ipr_cmd, 1, 3,
                          ioa_cfg->vpd_cbs_dma + offsetof(struct ipr_misc_cbs, page3_data),
@@ -6512,6 +6640,48 @@ static int ipr_reset_start_bist(struct ipr_cmnd *ipr_cmd)
        return rc;
 }
 
+/**
+ * ipr_reset_slot_reset_done - Clear PCI reset to the adapter
+ * @ipr_cmd:   ipr command struct
+ *
+ * Description: This clears PCI reset to the adapter and delays two seconds.
+ *
+ * Return value:
+ *     IPR_RC_JOB_RETURN
+ **/
+static int ipr_reset_slot_reset_done(struct ipr_cmnd *ipr_cmd)
+{
+       ENTER;
+       pci_set_pcie_reset_state(ipr_cmd->ioa_cfg->pdev, pcie_deassert_reset);
+       ipr_cmd->job_step = ipr_reset_bist_done;
+       ipr_reset_start_timer(ipr_cmd, IPR_WAIT_FOR_BIST_TIMEOUT);
+       LEAVE;
+       return IPR_RC_JOB_RETURN;
+}
+
+/**
+ * ipr_reset_slot_reset - Reset the PCI slot of the adapter.
+ * @ipr_cmd:   ipr command struct
+ *
+ * Description: This asserts PCI reset to the adapter.
+ *
+ * Return value:
+ *     IPR_RC_JOB_RETURN
+ **/
+static int ipr_reset_slot_reset(struct ipr_cmnd *ipr_cmd)
+{
+       struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
+       struct pci_dev *pdev = ioa_cfg->pdev;
+
+       ENTER;
+       pci_block_user_cfg_access(pdev);
+       pci_set_pcie_reset_state(pdev, pcie_warm_reset);
+       ipr_cmd->job_step = ipr_reset_slot_reset_done;
+       ipr_reset_start_timer(ipr_cmd, IPR_PCI_RESET_TIMEOUT);
+       LEAVE;
+       return IPR_RC_JOB_RETURN;
+}
+
 /**
  * ipr_reset_allowed - Query whether or not IOA can be reset
  * @ioa_cfg:   ioa config struct
@@ -6551,7 +6721,7 @@ static int ipr_reset_wait_to_start_bist(struct ipr_cmnd *ipr_cmd)
                ipr_cmd->u.time_left -= IPR_CHECK_FOR_RESET_TIMEOUT;
                ipr_reset_start_timer(ipr_cmd, IPR_CHECK_FOR_RESET_TIMEOUT);
        } else {
-               ipr_cmd->job_step = ipr_reset_start_bist;
+               ipr_cmd->job_step = ioa_cfg->reset;
                rc = IPR_RC_JOB_CONTINUE;
        }
 
@@ -6584,7 +6754,7 @@ static int ipr_reset_alert(struct ipr_cmnd *ipr_cmd)
                writel(IPR_UPROCI_RESET_ALERT, ioa_cfg->regs.set_uproc_interrupt_reg);
                ipr_cmd->job_step = ipr_reset_wait_to_start_bist;
        } else {
-               ipr_cmd->job_step = ipr_reset_start_bist;
+               ipr_cmd->job_step = ioa_cfg->reset;
        }
 
        ipr_cmd->u.time_left = IPR_WAIT_FOR_RESET_TIMEOUT;
@@ -6679,12 +6849,14 @@ static int ipr_reset_shutdown_ioa(struct ipr_cmnd *ipr_cmd)
                ipr_cmd->ioarcb.cmd_pkt.cdb[0] = IPR_IOA_SHUTDOWN;
                ipr_cmd->ioarcb.cmd_pkt.cdb[1] = shutdown_type;
 
-               if (shutdown_type == IPR_SHUTDOWN_ABBREV)
-                       timeout = IPR_ABBREV_SHUTDOWN_TIMEOUT;
+               if (shutdown_type == IPR_SHUTDOWN_NORMAL)
+                       timeout = IPR_SHUTDOWN_TIMEOUT;
                else if (shutdown_type == IPR_SHUTDOWN_PREPARE_FOR_NORMAL)
                        timeout = IPR_INTERNAL_TIMEOUT;
+               else if (ioa_cfg->dual_raid && ipr_dual_ioa_raid)
+                       timeout = IPR_DUAL_IOA_ABBR_SHUTDOWN_TO;
                else
-                       timeout = IPR_SHUTDOWN_TIMEOUT;
+                       timeout = IPR_ABBREV_SHUTDOWN_TIMEOUT;
 
                ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, timeout);
 
@@ -6864,8 +7036,11 @@ static pci_ers_result_t ipr_pci_slot_reset(struct pci_dev *pdev)
        struct ipr_ioa_cfg *ioa_cfg = pci_get_drvdata(pdev);
 
        spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
-       _ipr_initiate_ioa_reset(ioa_cfg, ipr_reset_restore_cfg_space,
-                                        IPR_SHUTDOWN_NONE);
+       if (ioa_cfg->needs_warm_reset)
+               ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE);
+       else
+               _ipr_initiate_ioa_reset(ioa_cfg, ipr_reset_restore_cfg_space,
+                                       IPR_SHUTDOWN_NONE);
        spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
        return PCI_ERS_RESULT_RECOVERED;
 }
@@ -7314,7 +7489,7 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
        unsigned long ipr_regs_pci;
        void __iomem *ipr_regs;
        int rc = PCIBIOS_SUCCESSFUL;
-       volatile u32 mask, uproc;
+       volatile u32 mask, uproc, interrupts;
 
        ENTER;
 
@@ -7353,6 +7528,14 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
        else
                ioa_cfg->transop_timeout = IPR_OPERATIONAL_TIMEOUT;
 
+       rc = pci_read_config_byte(pdev, PCI_REVISION_ID, &ioa_cfg->revid);
+
+       if (rc != PCIBIOS_SUCCESSFUL) {
+               dev_err(&pdev->dev, "Failed to read PCI revision ID\n");
+               rc = -EIO;
+               goto out_scsi_host_put;
+       }
+
        ipr_regs_pci = pci_resource_start(pdev, 0);
 
        rc = pci_request_regions(pdev, IPR_NAME);
@@ -7421,9 +7604,14 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
         * the card is in an unknown state and needs a hard reset
         */
        mask = readl(ioa_cfg->regs.sense_interrupt_mask_reg);
+       interrupts = readl(ioa_cfg->regs.sense_interrupt_reg);
        uproc = readl(ioa_cfg->regs.sense_uproc_interrupt_reg);
        if ((mask & IPR_PCII_HRRQ_UPDATED) == 0 || (uproc & IPR_UPROCI_RESET_ALERT))
                ioa_cfg->needs_hard_reset = 1;
+       if (interrupts & IPR_PCII_ERROR_INTERRUPTS)
+               ioa_cfg->needs_hard_reset = 1;
+       if (interrupts & IPR_PCII_IOA_UNIT_CHECKED)
+               ioa_cfg->ioa_unit_checked = 1;
 
        ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER);
        rc = request_irq(pdev->irq, ipr_isr, IRQF_SHARED, IPR_NAME, ioa_cfg);
@@ -7434,6 +7622,13 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
                goto cleanup_nolog;
        }
 
+       if ((dev_id->driver_data & IPR_USE_PCI_WARM_RESET) ||
+           (dev_id->device == PCI_DEVICE_ID_IBM_OBSIDIAN_E && !ioa_cfg->revid)) {
+               ioa_cfg->needs_warm_reset = 1;
+               ioa_cfg->reset = ipr_reset_slot_reset;
+       } else
+               ioa_cfg->reset = ipr_reset_start_bist;
+
        spin_lock(&ipr_driver_lock);
        list_add_tail(&ioa_cfg->queue, &ipr_ioa_head);
        spin_unlock(&ipr_driver_lock);
@@ -7700,7 +7895,7 @@ static struct pci_device_id ipr_pci_table[] __devinitdata = {
              PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57B3, 0, 0, 0 },
        { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN_E,
              PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57B7, 0, 0,
-             IPR_USE_LONG_TRANSOP_TIMEOUT },
+             IPR_USE_LONG_TRANSOP_TIMEOUT | IPR_USE_PCI_WARM_RESET },
        { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE,
                PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_2780, 0, 0, 0 },
        { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP,