From: Venkat Gopalakrishnan <venk...@codeaurora.org>

[ Upstream commit 9333d77573485c827b4c0fc960c840df3e5ce719 ]

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

Link: 
https://lore.kernel.org/r/1573798172-20534-6-git-send-email-c...@codeaurora.org
Reviewed-by: Avri Altman <avri.alt...@wdc.com>
Signed-off-by: Venkat Gopalakrishnan <venk...@codeaurora.org>
Signed-off-by: Can Guo <c...@codeaurora.org>
Signed-off-by: Martin K. Petersen <martin.peter...@oracle.com>
Signed-off-by: Sasha Levin <sas...@kernel.org>
---
 drivers/scsi/ufs/ufshcd.c | 134 ++++++++++++++++++++++++++++----------
 drivers/scsi/ufs/ufshci.h |   2 +-
 2 files changed, 100 insertions(+), 36 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 476ef8044ae5..289edf70ccb9 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -239,7 +239,7 @@ static struct ufs_dev_fix ufs_fixups[] = {
        END_FIX
 };
 
-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 int ufshcd_reset_and_restore(struct ufs_hba *hba);
 static int ufshcd_eh_host_reset_handler(struct scsi_cmnd *cmd);
@@ -4815,19 +4815,29 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct 
ufshcd_lrb *lrbp)
  * ufshcd_uic_cmd_compl - handle completion of uic command
  * @hba: per adapter instance
  * @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) {
                hba->active_uic_cmd->argument2 |=
                        ufshcd_get_uic_cmd_result(hba);
                hba->active_uic_cmd->argument3 =
                        ufshcd_get_dme_attr_val(hba);
                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);
+               retval = IRQ_HANDLED;
+       }
+       return retval;
 }
 
 /**
@@ -4883,8 +4893,12 @@ static void __ufshcd_transfer_req_compl(struct ufs_hba 
*hba,
 /**
  * ufshcd_transfer_req_compl - handle SCSI and query command completion
  * @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;
        u32 tr_doorbell;
@@ -4903,7 +4917,12 @@ static void ufshcd_transfer_req_compl(struct ufs_hba 
*hba)
        tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
        completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
 
-       __ufshcd_transfer_req_compl(hba, completed_reqs);
+       if (completed_reqs) {
+               __ufshcd_transfer_req_compl(hba, completed_reqs);
+               return IRQ_HANDLED;
+       } else {
+               return IRQ_NONE;
+       }
 }
 
 /**
@@ -5424,61 +5443,77 @@ out:
 /**
  * ufshcd_update_uic_error - check and set fatal UIC error flags.
  * @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;
+       irqreturn_t retval = IRQ_NONE;
 
        /* PHY layer lane error */
        reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER);
        /* Ignore LINERESET indication, as this is not an error */
        if ((reg & UIC_PHY_ADAPTER_LAYER_ERROR) &&
-                       (reg & UIC_PHY_ADAPTER_LAYER_LANE_ERR_MASK)) {
+           (reg & UIC_PHY_ADAPTER_LAYER_LANE_ERR_MASK)) {
                /*
                 * To know whether this error is fatal or not, DB timeout
                 * must be checked but this error is handled separately.
                 */
                dev_dbg(hba->dev, "%s: UIC Lane error reported\n", __func__);
                ufshcd_update_reg_hist(&hba->ufs_stats.pa_err, reg);
+               retval |= IRQ_HANDLED;
        }
 
        /* PA_INIT_ERROR is fatal and needs UIC reset */
        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_reg_hist(&hba->ufs_stats.dl_err, reg);
 
-       if (reg & UIC_DATA_LINK_LAYER_ERROR_PA_INIT)
-               hba->uic_error |= UFSHCD_UIC_DL_PA_INIT_ERROR;
-       else if (hba->dev_quirks &
-                  UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS) {
-               if (reg & UIC_DATA_LINK_LAYER_ERROR_NAC_RECEIVED)
-                       hba->uic_error |=
-                               UFSHCD_UIC_DL_NAC_RECEIVED_ERROR;
-               else if (reg & UIC_DATA_LINK_LAYER_ERROR_TCx_REPLAY_TIMEOUT)
-                       hba->uic_error |= UFSHCD_UIC_DL_TCx_REPLAY_ERROR;
+               if (reg & UIC_DATA_LINK_LAYER_ERROR_PA_INIT)
+                       hba->uic_error |= UFSHCD_UIC_DL_PA_INIT_ERROR;
+               else if (hba->dev_quirks &
+                               UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS) {
+                       if (reg & UIC_DATA_LINK_LAYER_ERROR_NAC_RECEIVED)
+                               hba->uic_error |=
+                                       UFSHCD_UIC_DL_NAC_RECEIVED_ERROR;
+                       else if (reg & 
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 */
        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_reg_hist(&hba->ufs_stats.nl_err, reg);
                hba->uic_error |= UFSHCD_UIC_NL_ERROR;
+               retval |= IRQ_HANDLED;
        }
 
        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_reg_hist(&hba->ufs_stats.tl_err, reg);
                hba->uic_error |= UFSHCD_UIC_TL_ERROR;
+               retval |= IRQ_HANDLED;
        }
 
        reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_DME);
-       if (reg) {
+       if ((reg & UIC_DME_ERROR) &&
+           (reg & UIC_DME_ERROR_CODE_MASK)) {
                ufshcd_update_reg_hist(&hba->ufs_stats.dme_err, reg);
                hba->uic_error |= UFSHCD_UIC_DME_ERROR;
+               retval |= IRQ_HANDLED;
        }
 
        dev_dbg(hba->dev, "%s: UIC error flags = 0x%08x\n",
                        __func__, hba->uic_error);
+       return retval;
 }
 
 static bool ufshcd_is_auto_hibern8_error(struct ufs_hba *hba,
@@ -5502,10 +5537,15 @@ static bool ufshcd_is_auto_hibern8_error(struct ufs_hba 
*hba,
 /**
  * ufshcd_check_errors - Check for errors that need s/w attention
  * @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;
+       irqreturn_t retval = IRQ_NONE;
 
        if (hba->errors & INT_FATAL_ERRORS) {
                ufshcd_update_reg_hist(&hba->ufs_stats.fatal_err, hba->errors);
@@ -5514,7 +5554,7 @@ static void ufshcd_check_errors(struct ufs_hba *hba)
 
        if (hba->errors & UIC_ERROR) {
                hba->uic_error = 0;
-               ufshcd_update_uic_error(hba);
+               retval = ufshcd_update_uic_error(hba);
                if (hba->uic_error)
                        queue_eh_work = true;
        }
@@ -5562,6 +5602,7 @@ static void ufshcd_check_errors(struct ufs_hba *hba)
                        }
                        schedule_work(&hba->eh_work);
                }
+               retval |= IRQ_HANDLED;
        }
        /*
         * if (!queue_eh_work) -
@@ -5569,44 +5610,62 @@ static void ufshcd_check_errors(struct ufs_hba *hba)
         * itself without s/w intervention or errors that will be
         * handled by the SCSI core layer.
         */
+       return retval;
 }
 
 /**
  * ufshcd_tmc_handler - handle task management function completion
  * @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;
 
        tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL);
        hba->tm_condition = tm_doorbell ^ hba->outstanding_tasks;
-       wake_up(&hba->tm_wq);
+       if (hba->tm_condition) {
+               wake_up(&hba->tm_wq);
+               return IRQ_HANDLED;
+       } else {
+               return IRQ_NONE;
+       }
 }
 
 /**
  * ufshcd_sl_intr - Interrupt service routine
  * @hba: per adapter instance
  * @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;
+
        hba->errors = UFSHCD_ERROR_MASK & intr_status;
 
        if (ufshcd_is_auto_hibern8_error(hba, intr_status))
                hba->errors |= (UFSHCD_UIC_HIBERN8_MASK & intr_status);
 
        if (hba->errors)
-               ufshcd_check_errors(hba);
+               retval |= ufshcd_check_errors(hba);
 
        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)
-               ufshcd_tmc_handler(hba);
+               retval |= ufshcd_tmc_handler(hba);
 
        if (intr_status & UTP_TRANSFER_REQ_COMPL)
-               ufshcd_transfer_req_compl(hba);
+               retval |= ufshcd_transfer_req_compl(hba);
+
+       return retval;
 }
 
 /**
@@ -5614,8 +5673,9 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 
intr_status)
  * @irq: irq number
  * @__hba: pointer to adapter instance
  *
- * Returns IRQ_HANDLED - If interrupt is valid
- *             IRQ_NONE - If invalid interrupt
+ * Returns
+ *  IRQ_HANDLED - If interrupt is valid
+ *  IRQ_NONE    - If invalid interrupt
  */
 static irqreturn_t ufshcd_intr(int irq, void *__hba)
 {
@@ -5638,14 +5698,18 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba)
                        intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
                if (intr_status)
                        ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS);
-               if (enabled_intr_status) {
-                       ufshcd_sl_intr(hba, enabled_intr_status);
-                       retval = IRQ_HANDLED;
-               }
+               if (enabled_intr_status)
+                       retval |= ufshcd_sl_intr(hba, enabled_intr_status);
 
                intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
        }
 
+       if (retval == IRQ_NONE) {
+               dev_err(hba->dev, "%s: Unhandled interrupt 0x%08x\n",
+                                       __func__, intr_status);
+               ufshcd_dump_regs(hba, 0, UFSHCI_REG_SPACE_SIZE, "host_regs: ");
+       }
+
        spin_unlock(hba->host->host_lock);
        return retval;
 }
diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h
index dbb75cd28dc8..c2961d37cc1c 100644
--- a/drivers/scsi/ufs/ufshci.h
+++ b/drivers/scsi/ufs/ufshci.h
@@ -195,7 +195,7 @@ enum {
 
 /* UECDL - Host UIC Error Code Data Link Layer 3Ch */
 #define UIC_DATA_LINK_LAYER_ERROR              0x80000000
-#define UIC_DATA_LINK_LAYER_ERROR_CODE_MASK    0x7FFF
+#define UIC_DATA_LINK_LAYER_ERROR_CODE_MASK    0xFFFF
 #define UIC_DATA_LINK_LAYER_ERROR_TCX_REP_TIMER_EXP    0x2
 #define UIC_DATA_LINK_LAYER_ERROR_AFCX_REQ_TIMER_EXP   0x4
 #define UIC_DATA_LINK_LAYER_ERROR_FCX_PRO_TIMER_EXP    0x8
-- 
2.30.2



Reply via email to