scsi: ufs: fix irq return code

Return IRQ_HANDLED only if the irq is really handled, this will
help in catching spurious interrupts that go unhandled.

Change-Id: Icc2a373f3f4599887c4f86632107ed11e0525153
Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
This commit is contained in:
Venkat Gopalakrishnan 2016-12-08 16:44:35 -08:00
parent de3c9e7c78
commit 61e6a9f4b8

View file

@ -350,7 +350,7 @@ static inline bool ufshcd_is_valid_pm_lvl(int lvl)
} }
static irqreturn_t ufshcd_intr(int irq, void *__hba); static irqreturn_t ufshcd_intr(int irq, void *__hba);
static void ufshcd_tmc_handler(struct ufs_hba *hba); static irqreturn_t ufshcd_tmc_handler(struct ufs_hba *hba);
static void ufshcd_async_scan(void *data, async_cookie_t cookie); static void ufshcd_async_scan(void *data, async_cookie_t cookie);
static int ufshcd_reset_and_restore(struct ufs_hba *hba); static int ufshcd_reset_and_restore(struct ufs_hba *hba);
static int ufshcd_eh_host_reset_handler(struct scsi_cmnd *cmd); static int ufshcd_eh_host_reset_handler(struct scsi_cmnd *cmd);
@ -5228,19 +5228,29 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
* ufshcd_uic_cmd_compl - handle completion of uic command * ufshcd_uic_cmd_compl - handle completion of uic command
* @hba: per adapter instance * @hba: per adapter instance
* @intr_status: interrupt status generated by the controller * @intr_status: interrupt status generated by the controller
*
* Returns
* IRQ_HANDLED - If interrupt is valid
* IRQ_NONE - If invalid interrupt
*/ */
static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status) static irqreturn_t ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status)
{ {
irqreturn_t retval = IRQ_NONE;
if ((intr_status & UIC_COMMAND_COMPL) && hba->active_uic_cmd) { if ((intr_status & UIC_COMMAND_COMPL) && hba->active_uic_cmd) {
hba->active_uic_cmd->argument2 |= hba->active_uic_cmd->argument2 |=
ufshcd_get_uic_cmd_result(hba); ufshcd_get_uic_cmd_result(hba);
hba->active_uic_cmd->argument3 = hba->active_uic_cmd->argument3 =
ufshcd_get_dme_attr_val(hba); ufshcd_get_dme_attr_val(hba);
complete(&hba->active_uic_cmd->done); complete(&hba->active_uic_cmd->done);
retval = IRQ_HANDLED;
} }
if ((intr_status & UFSHCD_UIC_PWR_MASK) && hba->uic_async_done) if ((intr_status & UFSHCD_UIC_PWR_MASK) && hba->uic_async_done) {
complete(hba->uic_async_done); complete(hba->uic_async_done);
retval = IRQ_HANDLED;
}
return retval;
} }
/** /**
@ -5367,8 +5377,12 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba *hba,
/** /**
* ufshcd_transfer_req_compl - handle SCSI and query command completion * ufshcd_transfer_req_compl - handle SCSI and query command completion
* @hba: per adapter instance * @hba: per adapter instance
*
* Returns
* IRQ_HANDLED - If interrupt is valid
* IRQ_NONE - If invalid interrupt
*/ */
static void ufshcd_transfer_req_compl(struct ufs_hba *hba) static irqreturn_t ufshcd_transfer_req_compl(struct ufs_hba *hba)
{ {
unsigned long completed_reqs; unsigned long completed_reqs;
u32 tr_doorbell; u32 tr_doorbell;
@ -5386,7 +5400,12 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
completed_reqs = tr_doorbell ^ hba->outstanding_reqs; completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
if (completed_reqs) {
__ufshcd_transfer_req_compl(hba, completed_reqs); __ufshcd_transfer_req_compl(hba, completed_reqs);
return IRQ_HANDLED;
} else {
return IRQ_NONE;
}
} }
/** /**
@ -5964,10 +5983,15 @@ static void ufshcd_update_uic_reg_hist(struct ufs_uic_err_reg_hist *reg_hist,
/** /**
* ufshcd_update_uic_error - check and set fatal UIC error flags. * ufshcd_update_uic_error - check and set fatal UIC error flags.
* @hba: per-adapter instance * @hba: per-adapter instance
*
* Returns
* IRQ_HANDLED - If interrupt is valid
* IRQ_NONE - If invalid interrupt
*/ */
static void ufshcd_update_uic_error(struct ufs_hba *hba) static irqreturn_t ufshcd_update_uic_error(struct ufs_hba *hba)
{ {
u32 reg; u32 reg;
irqreturn_t retval = IRQ_NONE;
/* PHY layer lane error */ /* PHY layer lane error */
reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER); reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER);
@ -5994,11 +6018,13 @@ static void ufshcd_update_uic_error(struct ufs_hba *hba)
} }
} }
} }
retval |= IRQ_HANDLED;
} }
/* PA_INIT_ERROR is fatal and needs UIC reset */ /* PA_INIT_ERROR is fatal and needs UIC reset */
reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_DATA_LINK_LAYER); reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_DATA_LINK_LAYER);
if (reg) if ((reg & UIC_DATA_LINK_LAYER_ERROR) &&
(reg & UIC_DATA_LINK_LAYER_ERROR_CODE_MASK)) {
ufshcd_update_uic_reg_hist(&hba->ufs_stats.dl_err, reg); ufshcd_update_uic_reg_hist(&hba->ufs_stats.dl_err, reg);
if (reg & UIC_DATA_LINK_LAYER_ERROR_PA_INIT) { if (reg & UIC_DATA_LINK_LAYER_ERROR_PA_INIT) {
@ -6008,47 +6034,63 @@ static void ufshcd_update_uic_error(struct ufs_hba *hba)
if (reg & UIC_DATA_LINK_LAYER_ERROR_NAC_RECEIVED) if (reg & UIC_DATA_LINK_LAYER_ERROR_NAC_RECEIVED)
hba->uic_error |= hba->uic_error |=
UFSHCD_UIC_DL_NAC_RECEIVED_ERROR; UFSHCD_UIC_DL_NAC_RECEIVED_ERROR;
else if (reg & UIC_DATA_LINK_LAYER_ERROR_TCx_REPLAY_TIMEOUT) else if (reg &
hba->uic_error |= UFSHCD_UIC_DL_TCx_REPLAY_ERROR; UIC_DATA_LINK_LAYER_ERROR_TCx_REPLAY_TIMEOUT)
hba->uic_error |=
UFSHCD_UIC_DL_TCx_REPLAY_ERROR;
}
retval |= IRQ_HANDLED;
} }
/* UIC NL/TL/DME errors needs software retry */ /* UIC NL/TL/DME errors needs software retry */
reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_NETWORK_LAYER); reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_NETWORK_LAYER);
if (reg) { if ((reg & UIC_NETWORK_LAYER_ERROR) &&
(reg & UIC_NETWORK_LAYER_ERROR_CODE_MASK)) {
ufshcd_update_uic_reg_hist(&hba->ufs_stats.nl_err, reg); ufshcd_update_uic_reg_hist(&hba->ufs_stats.nl_err, reg);
hba->uic_error |= UFSHCD_UIC_NL_ERROR; hba->uic_error |= UFSHCD_UIC_NL_ERROR;
retval |= IRQ_HANDLED;
} }
reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_TRANSPORT_LAYER); reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_TRANSPORT_LAYER);
if (reg) { if ((reg & UIC_TRANSPORT_LAYER_ERROR) &&
(reg & UIC_TRANSPORT_LAYER_ERROR_CODE_MASK)) {
ufshcd_update_uic_reg_hist(&hba->ufs_stats.tl_err, reg); ufshcd_update_uic_reg_hist(&hba->ufs_stats.tl_err, reg);
hba->uic_error |= UFSHCD_UIC_TL_ERROR; hba->uic_error |= UFSHCD_UIC_TL_ERROR;
retval |= IRQ_HANDLED;
} }
reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_DME); reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_DME);
if (reg) { if ((reg & UIC_DME_ERROR) &&
(reg & UIC_DME_ERROR_CODE_MASK)) {
ufshcd_update_uic_reg_hist(&hba->ufs_stats.dme_err, reg); ufshcd_update_uic_reg_hist(&hba->ufs_stats.dme_err, reg);
hba->uic_error |= UFSHCD_UIC_DME_ERROR; hba->uic_error |= UFSHCD_UIC_DME_ERROR;
retval |= IRQ_HANDLED;
} }
dev_dbg(hba->dev, "%s: UIC error flags = 0x%08x\n", dev_dbg(hba->dev, "%s: UIC error flags = 0x%08x\n",
__func__, hba->uic_error); __func__, hba->uic_error);
return retval;
} }
/** /**
* ufshcd_check_errors - Check for errors that need s/w attention * ufshcd_check_errors - Check for errors that need s/w attention
* @hba: per-adapter instance * @hba: per-adapter instance
*
* Returns
* IRQ_HANDLED - If interrupt is valid
* IRQ_NONE - If invalid interrupt
*/ */
static void ufshcd_check_errors(struct ufs_hba *hba) static irqreturn_t ufshcd_check_errors(struct ufs_hba *hba)
{ {
bool queue_eh_work = false; bool queue_eh_work = false;
irqreturn_t retval = IRQ_NONE;
if (hba->errors & INT_FATAL_ERRORS || hba->ce_error) if (hba->errors & INT_FATAL_ERRORS || hba->ce_error)
queue_eh_work = true; queue_eh_work = true;
if (hba->errors & UIC_ERROR) { if (hba->errors & UIC_ERROR) {
hba->uic_error = 0; hba->uic_error = 0;
ufshcd_update_uic_error(hba); retval = ufshcd_update_uic_error(hba);
if (hba->uic_error) if (hba->uic_error)
queue_eh_work = true; queue_eh_work = true;
} }
@ -6073,6 +6115,7 @@ static void ufshcd_check_errors(struct ufs_hba *hba)
hba->ufshcd_state = UFSHCD_STATE_ERROR; hba->ufshcd_state = UFSHCD_STATE_ERROR;
schedule_work(&hba->eh_work); schedule_work(&hba->eh_work);
} }
retval |= IRQ_HANDLED;
} }
/* /*
* if (!queue_eh_work) - * if (!queue_eh_work) -
@ -6080,28 +6123,44 @@ static void ufshcd_check_errors(struct ufs_hba *hba)
* itself without s/w intervention or errors that will be * itself without s/w intervention or errors that will be
* handled by the SCSI core layer. * handled by the SCSI core layer.
*/ */
return retval;
} }
/** /**
* ufshcd_tmc_handler - handle task management function completion * ufshcd_tmc_handler - handle task management function completion
* @hba: per adapter instance * @hba: per adapter instance
*
* Returns
* IRQ_HANDLED - If interrupt is valid
* IRQ_NONE - If invalid interrupt
*/ */
static void ufshcd_tmc_handler(struct ufs_hba *hba) static irqreturn_t ufshcd_tmc_handler(struct ufs_hba *hba)
{ {
u32 tm_doorbell; u32 tm_doorbell;
tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL); tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL);
hba->tm_condition = tm_doorbell ^ hba->outstanding_tasks; hba->tm_condition = tm_doorbell ^ hba->outstanding_tasks;
if (hba->tm_condition) {
wake_up(&hba->tm_wq); wake_up(&hba->tm_wq);
return IRQ_HANDLED;
} else {
return IRQ_NONE;
}
} }
/** /**
* ufshcd_sl_intr - Interrupt service routine * ufshcd_sl_intr - Interrupt service routine
* @hba: per adapter instance * @hba: per adapter instance
* @intr_status: contains interrupts generated by the controller * @intr_status: contains interrupts generated by the controller
*
* Returns
* IRQ_HANDLED - If interrupt is valid
* IRQ_NONE - If invalid interrupt
*/ */
static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status) static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
{ {
irqreturn_t retval = IRQ_NONE;
ufsdbg_error_inject_dispatcher(hba, ufsdbg_error_inject_dispatcher(hba,
ERR_INJECT_INTR, intr_status, &intr_status); ERR_INJECT_INTR, intr_status, &intr_status);
@ -6109,16 +6168,18 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
hba->errors = UFSHCD_ERROR_MASK & intr_status; hba->errors = UFSHCD_ERROR_MASK & intr_status;
if (hba->errors || hba->ce_error) if (hba->errors || hba->ce_error)
ufshcd_check_errors(hba); retval |= ufshcd_check_errors(hba);
if (intr_status & UFSHCD_UIC_MASK) if (intr_status & UFSHCD_UIC_MASK)
ufshcd_uic_cmd_compl(hba, intr_status); retval |= ufshcd_uic_cmd_compl(hba, intr_status);
if (intr_status & UTP_TASK_REQ_COMPL) if (intr_status & UTP_TASK_REQ_COMPL)
ufshcd_tmc_handler(hba); retval |= ufshcd_tmc_handler(hba);
if (intr_status & UTP_TRANSFER_REQ_COMPL) if (intr_status & UTP_TRANSFER_REQ_COMPL)
ufshcd_transfer_req_compl(hba); retval |= ufshcd_transfer_req_compl(hba);
return retval;
} }
/** /**
@ -6126,7 +6187,8 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
* @irq: irq number * @irq: irq number
* @__hba: pointer to adapter instance * @__hba: pointer to adapter instance
* *
* Returns IRQ_HANDLED - If interrupt is valid * Returns
* IRQ_HANDLED - If interrupt is valid
* IRQ_NONE - If invalid interrupt * IRQ_NONE - If invalid interrupt
*/ */
static irqreturn_t ufshcd_intr(int irq, void *__hba) static irqreturn_t ufshcd_intr(int irq, void *__hba)
@ -6150,14 +6212,19 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba)
intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE); intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
if (intr_status) if (intr_status)
ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS);
if (enabled_intr_status) { if (enabled_intr_status)
ufshcd_sl_intr(hba, enabled_intr_status); retval |= ufshcd_sl_intr(hba, enabled_intr_status);
retval = IRQ_HANDLED;
}
intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
} while (intr_status && --retries); } while (intr_status && --retries);
if (retval == IRQ_NONE) {
dev_err(hba->dev, "%s: Unhandled interrupt 0x%08x\n",
__func__, intr_status);
ufshcd_hex_dump("host regs: ", hba->mmio_base,
UFSHCI_REG_SPACE_SIZE);
}
spin_unlock(hba->host->host_lock); spin_unlock(hba->host->host_lock);
return retval; return retval;
} }