summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2017-01-17 17:18:29 -0800
committerGerrit - the friendly Code Review server <code-review@localhost>2017-01-17 17:18:28 -0800
commitd9ac8efbd96e3d5106203f5faab87a38f4f87860 (patch)
treebe056f9b1323b68e5f10abc461a8d864eaa35235
parent4d29daa1dc8e6f5c7456a29d14f99bca6d903d89 (diff)
parent49aeb164758c60b1c067ef2a8b734a3f5cb7968d (diff)
Merge "scsi: ufs: handle auto hibern8 failure"
-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 d46682bb1d84..1e6db2a76fa5 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>
@@ -5264,9 +5264,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;
}
@@ -5858,6 +5877,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);
@@ -5867,6 +5887,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);
@@ -6003,6 +6039,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);