mirror of
https://github.com/torvalds/linux.git
synced 2024-10-31 17:21:49 +00:00
[SCSI] qla2xxx: Add PCI error recovery support.
Additional cleanups and Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com> Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
This commit is contained in:
parent
b7cc176c9e
commit
14e660e677
@ -23,6 +23,7 @@
|
|||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
#include <linux/firmware.h>
|
#include <linux/firmware.h>
|
||||||
|
#include <linux/aer.h>
|
||||||
#include <asm/semaphore.h>
|
#include <asm/semaphore.h>
|
||||||
|
|
||||||
#include <scsi/scsi.h>
|
#include <scsi/scsi.h>
|
||||||
|
@ -34,6 +34,7 @@ qla2100_intr_handler(int irq, void *dev_id)
|
|||||||
int status;
|
int status;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned long iter;
|
unsigned long iter;
|
||||||
|
uint16_t hccr;
|
||||||
uint16_t mb[4];
|
uint16_t mb[4];
|
||||||
|
|
||||||
ha = (scsi_qla_host_t *) dev_id;
|
ha = (scsi_qla_host_t *) dev_id;
|
||||||
@ -48,7 +49,23 @@ qla2100_intr_handler(int irq, void *dev_id)
|
|||||||
|
|
||||||
spin_lock_irqsave(&ha->hardware_lock, flags);
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
||||||
for (iter = 50; iter--; ) {
|
for (iter = 50; iter--; ) {
|
||||||
if ((RD_REG_WORD(®->istatus) & ISR_RISC_INT) == 0)
|
hccr = RD_REG_WORD(®->hccr);
|
||||||
|
if (hccr & HCCR_RISC_PAUSE) {
|
||||||
|
if (pci_channel_offline(ha->pdev))
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Issue a "HARD" reset in order for the RISC interrupt
|
||||||
|
* bit to be cleared. Schedule a big hammmer to get
|
||||||
|
* out of the RISC PAUSED state.
|
||||||
|
*/
|
||||||
|
WRT_REG_WORD(®->hccr, HCCR_RESET_RISC);
|
||||||
|
RD_REG_WORD(®->hccr);
|
||||||
|
|
||||||
|
ha->isp_ops->fw_dump(ha, 1);
|
||||||
|
set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
|
||||||
|
break;
|
||||||
|
} else if ((RD_REG_WORD(®->istatus) & ISR_RISC_INT) == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (RD_REG_WORD(®->semaphore) & BIT_0) {
|
if (RD_REG_WORD(®->semaphore) & BIT_0) {
|
||||||
@ -127,6 +144,9 @@ qla2300_intr_handler(int irq, void *dev_id)
|
|||||||
for (iter = 50; iter--; ) {
|
for (iter = 50; iter--; ) {
|
||||||
stat = RD_REG_DWORD(®->u.isp2300.host_status);
|
stat = RD_REG_DWORD(®->u.isp2300.host_status);
|
||||||
if (stat & HSR_RISC_PAUSED) {
|
if (stat & HSR_RISC_PAUSED) {
|
||||||
|
if (pci_channel_offline(ha->pdev))
|
||||||
|
break;
|
||||||
|
|
||||||
hccr = RD_REG_WORD(®->hccr);
|
hccr = RD_REG_WORD(®->hccr);
|
||||||
if (hccr & (BIT_15 | BIT_13 | BIT_11 | BIT_8))
|
if (hccr & (BIT_15 | BIT_13 | BIT_11 | BIT_8))
|
||||||
qla_printk(KERN_INFO, ha, "Parity error -- "
|
qla_printk(KERN_INFO, ha, "Parity error -- "
|
||||||
@ -1499,6 +1519,9 @@ qla24xx_intr_handler(int irq, void *dev_id)
|
|||||||
for (iter = 50; iter--; ) {
|
for (iter = 50; iter--; ) {
|
||||||
stat = RD_REG_DWORD(®->host_status);
|
stat = RD_REG_DWORD(®->host_status);
|
||||||
if (stat & HSRX_RISC_PAUSED) {
|
if (stat & HSRX_RISC_PAUSED) {
|
||||||
|
if (pci_channel_offline(ha->pdev))
|
||||||
|
break;
|
||||||
|
|
||||||
hccr = RD_REG_DWORD(®->hccr);
|
hccr = RD_REG_DWORD(®->hccr);
|
||||||
|
|
||||||
qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, "
|
qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, "
|
||||||
@ -1633,6 +1656,9 @@ qla24xx_msix_default(int irq, void *dev_id)
|
|||||||
for (iter = 50; iter--; ) {
|
for (iter = 50; iter--; ) {
|
||||||
stat = RD_REG_DWORD(®->host_status);
|
stat = RD_REG_DWORD(®->host_status);
|
||||||
if (stat & HSRX_RISC_PAUSED) {
|
if (stat & HSRX_RISC_PAUSED) {
|
||||||
|
if (pci_channel_offline(ha->pdev))
|
||||||
|
break;
|
||||||
|
|
||||||
hccr = RD_REG_DWORD(®->hccr);
|
hccr = RD_REG_DWORD(®->hccr);
|
||||||
|
|
||||||
qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, "
|
qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, "
|
||||||
|
@ -385,6 +385,11 @@ qla2x00_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
|
|||||||
srb_t *sp;
|
srb_t *sp;
|
||||||
int rval;
|
int rval;
|
||||||
|
|
||||||
|
if (unlikely(pci_channel_offline(ha->pdev))) {
|
||||||
|
cmd->result = DID_REQUEUE << 16;
|
||||||
|
goto qc_fail_command;
|
||||||
|
}
|
||||||
|
|
||||||
rval = fc_remote_port_chkready(rport);
|
rval = fc_remote_port_chkready(rport);
|
||||||
if (rval) {
|
if (rval) {
|
||||||
cmd->result = rval;
|
cmd->result = rval;
|
||||||
@ -447,6 +452,11 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
|
|||||||
int rval;
|
int rval;
|
||||||
scsi_qla_host_t *pha = to_qla_parent(ha);
|
scsi_qla_host_t *pha = to_qla_parent(ha);
|
||||||
|
|
||||||
|
if (unlikely(pci_channel_offline(ha->pdev))) {
|
||||||
|
cmd->result = DID_REQUEUE << 16;
|
||||||
|
goto qc24_fail_command;
|
||||||
|
}
|
||||||
|
|
||||||
rval = fc_remote_port_chkready(rport);
|
rval = fc_remote_port_chkready(rport);
|
||||||
if (rval) {
|
if (rval) {
|
||||||
cmd->result = rval;
|
cmd->result = rval;
|
||||||
@ -1571,6 +1581,10 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||||||
if (pci_enable_device(pdev))
|
if (pci_enable_device(pdev))
|
||||||
goto probe_out;
|
goto probe_out;
|
||||||
|
|
||||||
|
if (pci_find_aer_capability(pdev))
|
||||||
|
if (pci_enable_pcie_error_reporting(pdev))
|
||||||
|
goto probe_out;
|
||||||
|
|
||||||
sht = &qla2x00_driver_template;
|
sht = &qla2x00_driver_template;
|
||||||
if (pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2422 ||
|
if (pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2422 ||
|
||||||
pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2432 ||
|
pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2432 ||
|
||||||
@ -2814,6 +2828,105 @@ qla2x00_release_firmware(void)
|
|||||||
up(&qla_fw_lock);
|
up(&qla_fw_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static pci_ers_result_t
|
||||||
|
qla2xxx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
|
||||||
|
{
|
||||||
|
switch (state) {
|
||||||
|
case pci_channel_io_normal:
|
||||||
|
return PCI_ERS_RESULT_CAN_RECOVER;
|
||||||
|
case pci_channel_io_frozen:
|
||||||
|
pci_disable_device(pdev);
|
||||||
|
return PCI_ERS_RESULT_NEED_RESET;
|
||||||
|
case pci_channel_io_perm_failure:
|
||||||
|
qla2x00_remove_one(pdev);
|
||||||
|
return PCI_ERS_RESULT_DISCONNECT;
|
||||||
|
}
|
||||||
|
return PCI_ERS_RESULT_NEED_RESET;
|
||||||
|
}
|
||||||
|
|
||||||
|
static pci_ers_result_t
|
||||||
|
qla2xxx_pci_mmio_enabled(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
int risc_paused = 0;
|
||||||
|
uint32_t stat;
|
||||||
|
unsigned long flags;
|
||||||
|
scsi_qla_host_t *ha = pci_get_drvdata(pdev);
|
||||||
|
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
|
||||||
|
struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&ha->hardware_lock, flags);
|
||||||
|
if (IS_QLA2100(ha) || IS_QLA2200(ha)){
|
||||||
|
stat = RD_REG_DWORD(®->hccr);
|
||||||
|
if (stat & HCCR_RISC_PAUSE)
|
||||||
|
risc_paused = 1;
|
||||||
|
} else if (IS_QLA23XX(ha)) {
|
||||||
|
stat = RD_REG_DWORD(®->u.isp2300.host_status);
|
||||||
|
if (stat & HSR_RISC_PAUSED)
|
||||||
|
risc_paused = 1;
|
||||||
|
} else if (IS_FWI2_CAPABLE(ha)) {
|
||||||
|
stat = RD_REG_DWORD(®24->host_status);
|
||||||
|
if (stat & HSRX_RISC_PAUSED)
|
||||||
|
risc_paused = 1;
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
||||||
|
|
||||||
|
if (risc_paused) {
|
||||||
|
qla_printk(KERN_INFO, ha, "RISC paused -- mmio_enabled, "
|
||||||
|
"Dumping firmware!\n");
|
||||||
|
ha->isp_ops->fw_dump(ha, 0);
|
||||||
|
|
||||||
|
return PCI_ERS_RESULT_NEED_RESET;
|
||||||
|
} else
|
||||||
|
return PCI_ERS_RESULT_RECOVERED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static pci_ers_result_t
|
||||||
|
qla2xxx_pci_slot_reset(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
pci_ers_result_t ret = PCI_ERS_RESULT_DISCONNECT;
|
||||||
|
scsi_qla_host_t *ha = pci_get_drvdata(pdev);
|
||||||
|
|
||||||
|
if (pci_enable_device(pdev)) {
|
||||||
|
qla_printk(KERN_WARNING, ha,
|
||||||
|
"Can't re-enable PCI device after reset.\n");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
pci_set_master(pdev);
|
||||||
|
|
||||||
|
if (ha->isp_ops->pci_config(ha))
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
set_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
|
||||||
|
if (qla2x00_abort_isp(ha)== QLA_SUCCESS)
|
||||||
|
ret = PCI_ERS_RESULT_RECOVERED;
|
||||||
|
clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
qla2xxx_pci_resume(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
scsi_qla_host_t *ha = pci_get_drvdata(pdev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = qla2x00_wait_for_hba_online(ha);
|
||||||
|
if (ret != QLA_SUCCESS) {
|
||||||
|
qla_printk(KERN_ERR, ha,
|
||||||
|
"the device failed to resume I/O "
|
||||||
|
"from slot/link_reset");
|
||||||
|
}
|
||||||
|
pci_cleanup_aer_uncorrect_error_status(pdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pci_error_handlers qla2xxx_err_handler = {
|
||||||
|
.error_detected = qla2xxx_pci_error_detected,
|
||||||
|
.mmio_enabled = qla2xxx_pci_mmio_enabled,
|
||||||
|
.slot_reset = qla2xxx_pci_slot_reset,
|
||||||
|
.resume = qla2xxx_pci_resume,
|
||||||
|
};
|
||||||
|
|
||||||
static struct pci_device_id qla2xxx_pci_tbl[] = {
|
static struct pci_device_id qla2xxx_pci_tbl[] = {
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2100) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2100) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2200) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2200) },
|
||||||
@ -2839,6 +2952,7 @@ static struct pci_driver qla2xxx_pci_driver = {
|
|||||||
.id_table = qla2xxx_pci_tbl,
|
.id_table = qla2xxx_pci_tbl,
|
||||||
.probe = qla2x00_probe_one,
|
.probe = qla2x00_probe_one,
|
||||||
.remove = __devexit_p(qla2x00_remove_one),
|
.remove = __devexit_p(qla2x00_remove_one),
|
||||||
|
.err_handler = &qla2xxx_err_handler,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user