From 3feeb89d40cc0ab3777f12571509b23da466105c Mon Sep 17 00:00:00 2001 From: Wayne Boyer Date: Tue, 20 Oct 2009 11:09:00 -0700 Subject: [PATCH] [SCSI] ipr: add workaround for MSI interrupts on P7 This patch adds some additional logic to the interrupt service routine to fix a potential problem where an MSI interrupt does not get cleared the first time. Signed-off-by: Wayne Boyer Acked-by: Brian King Signed-off-by: James Bottomley --- drivers/scsi/ipr.c | 42 +++++++++++++++++++++++++++++++++--------- drivers/scsi/ipr.h | 1 + 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 5f045505a1f4..76d294fc7846 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -4188,6 +4188,25 @@ static irqreturn_t ipr_handle_other_interrupt(struct ipr_ioa_cfg *ioa_cfg, return rc; } +/** + * ipr_isr_eh - Interrupt service routine error handler + * @ioa_cfg: ioa config struct + * @msg: message to log + * + * Return value: + * none + **/ +static void ipr_isr_eh(struct ipr_ioa_cfg *ioa_cfg, char *msg) +{ + ioa_cfg->errors_logged++; + dev_err(&ioa_cfg->pdev->dev, "%s\n", msg); + + if (WAIT_FOR_DUMP == ioa_cfg->sdt_state) + ioa_cfg->sdt_state = GET_DUMP; + + ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE); +} + /** * ipr_isr - Interrupt service routine * @irq: irq number @@ -4203,6 +4222,7 @@ static irqreturn_t ipr_isr(int irq, void *devp) volatile u32 int_reg, int_mask_reg; u32 ioasc; u16 cmd_index; + int num_hrrq = 0; struct ipr_cmnd *ipr_cmd; irqreturn_t rc = IRQ_NONE; @@ -4233,13 +4253,7 @@ static irqreturn_t ipr_isr(int irq, void *devp) IPR_HRRQ_REQ_RESP_HANDLE_MASK) >> IPR_HRRQ_REQ_RESP_HANDLE_SHIFT; if (unlikely(cmd_index >= IPR_NUM_CMD_BLKS)) { - ioa_cfg->errors_logged++; - dev_err(&ioa_cfg->pdev->dev, "Invalid response handle from IOA\n"); - - if (WAIT_FOR_DUMP == ioa_cfg->sdt_state) - ioa_cfg->sdt_state = GET_DUMP; - - ipr_initiate_ioa_reset(ioa_cfg, IPR_SHUTDOWN_NONE); + ipr_isr_eh(ioa_cfg, "Invalid response handle from IOA"); spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); return IRQ_HANDLED; } @@ -4266,8 +4280,18 @@ static irqreturn_t ipr_isr(int irq, void *devp) if (ipr_cmd != NULL) { /* Clear the PCI interrupt */ - writel(IPR_PCII_HRRQ_UPDATED, ioa_cfg->regs.clr_interrupt_reg); - int_reg = readl(ioa_cfg->regs.sense_interrupt_reg) & ~int_mask_reg; + do { + writel(IPR_PCII_HRRQ_UPDATED, ioa_cfg->regs.clr_interrupt_reg); + int_reg = readl(ioa_cfg->regs.sense_interrupt_reg) & ~int_mask_reg; + } while (int_reg & IPR_PCII_HRRQ_UPDATED && + num_hrrq++ < IPR_MAX_HRRQ_RETRIES); + + if (int_reg & IPR_PCII_HRRQ_UPDATED) { + ipr_isr_eh(ioa_cfg, "Error clearing HRRQ"); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return IRQ_HANDLED; + } + } else break; } diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index 163245a1c3e5..19bbcf39f0c9 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -144,6 +144,7 @@ #define IPR_IOA_MAX_SECTORS 32767 #define IPR_VSET_MAX_SECTORS 512 #define IPR_MAX_CDB_LEN 16 +#define IPR_MAX_HRRQ_RETRIES 3 #define IPR_DEFAULT_BUS_WIDTH 16 #define IPR_80MBs_SCSI_RATE ((80 * 10) / (IPR_DEFAULT_BUS_WIDTH / 8))