diff options
| author | Linux Build Service Account <lnxbuild@localhost> | 2017-01-17 17:18:29 -0800 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2017-01-17 17:18:28 -0800 |
| commit | d9ac8efbd96e3d5106203f5faab87a38f4f87860 (patch) | |
| tree | be056f9b1323b68e5f10abc461a8d864eaa35235 | |
| parent | 4d29daa1dc8e6f5c7456a29d14f99bca6d903d89 (diff) | |
| parent | 49aeb164758c60b1c067ef2a8b734a3f5cb7968d (diff) | |
Merge "scsi: ufs: handle auto hibern8 failure"
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 47 |
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); |
