summaryrefslogtreecommitdiff
path: root/drivers/scsi/ufs
diff options
context:
space:
mode:
authorVenkat Gopalakrishnan <venkatg@codeaurora.org>2017-01-03 17:31:53 -0800
committerVenkat Gopalakrishnan <venkatg@codeaurora.org>2017-01-06 17:17:25 -0800
commit49aeb164758c60b1c067ef2a8b734a3f5cb7968d (patch)
tree966486b0b7dc6b826a672d4e451437bb9d17f339 /drivers/scsi/ufs
parent580c14135565072fe06e965b76592e67019a1248 (diff)
scsi: ufs: handle auto hibern8 failure
With the support of auto hibern8 it's possible that we could get an error interrupt raised when there is no software initiated request, handle this error by resetting the host controller followed by a full link recovery. Change-Id: Ice74ce7c469e96f61f78a5040f726d7bdb23e44e Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
Diffstat (limited to 'drivers/scsi/ufs')
-rw-r--r--drivers/scsi/ufs/ufshcd.c47
1 files changed, 43 insertions, 4 deletions
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 6e88e4b11273..34a09dc926b1 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3,7 +3,7 @@
*
* This code is based on drivers/scsi/ufs/ufshcd.c
* Copyright (C) 2011-2013 Samsung India Software Operations
- * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
*
* Authors:
* Santosh Yaraganavi <santosh.sy@samsung.com>
@@ -5257,9 +5257,28 @@ static irqreturn_t ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status)
retval = IRQ_HANDLED;
}
- if ((intr_status & UFSHCD_UIC_PWR_MASK) && hba->uic_async_done) {
- complete(hba->uic_async_done);
- retval = IRQ_HANDLED;
+ if (intr_status & UFSHCD_UIC_PWR_MASK) {
+ if (hba->uic_async_done) {
+ complete(hba->uic_async_done);
+ retval = IRQ_HANDLED;
+ } else if (ufshcd_is_auto_hibern8_supported(hba)) {
+ /*
+ * If uic_async_done flag is not set then this
+ * is an Auto hibern8 err interrupt.
+ * Perform a host reset followed by a full
+ * link recovery.
+ */
+ hba->ufshcd_state = UFSHCD_STATE_ERROR;
+ hba->force_host_reset = true;
+ dev_err(hba->dev, "%s: Auto Hibern8 %s failed - status: 0x%08x, upmcrs: 0x%08x\n",
+ __func__, (intr_status & UIC_HIBERNATE_ENTER) ?
+ "Enter" : "Exit",
+ intr_status, ufshcd_get_upmcrs(hba));
+ __ufshcd_print_host_regs(hba, true);
+ ufshcd_print_host_state(hba);
+ schedule_work(&hba->eh_work);
+ retval = IRQ_HANDLED;
+ }
}
return retval;
}
@@ -5851,6 +5870,7 @@ static void ufshcd_err_handler(struct work_struct *work)
int err = 0;
int tag;
bool needs_reset = false;
+ bool clks_enabled = false;
hba = container_of(work, struct ufs_hba, eh_work);
@@ -5860,6 +5880,22 @@ static void ufshcd_err_handler(struct work_struct *work)
if (hba->ufshcd_state == UFSHCD_STATE_RESET)
goto out;
+ /*
+ * Make sure the clocks are ON before we proceed with err
+ * handling. For the majority of cases err handler would be
+ * run with clocks ON. There is a possibility that the err
+ * handler was scheduled due to auto hibern8 error interrupt,
+ * in which case the clocks could be gated or be in the
+ * process of gating when the err handler runs.
+ */
+ if (unlikely((hba->clk_gating.state != CLKS_ON) &&
+ ufshcd_is_auto_hibern8_supported(hba))) {
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ ufshcd_hold(hba, false);
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ clks_enabled = true;
+ }
+
hba->ufshcd_state = UFSHCD_STATE_RESET;
ufshcd_set_eh_in_progress(hba);
@@ -5996,6 +6032,9 @@ skip_err_handling:
}
hba->silence_err_logs = false;
+
+ if (clks_enabled)
+ __ufshcd_release(hba, false);
out:
ufshcd_clear_eh_in_progress(hba);
spin_unlock_irqrestore(hba->host->host_lock, flags);