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>
This commit is contained in:
Venkat Gopalakrishnan 2017-01-03 17:31:53 -08:00
parent 580c141355
commit 49aeb16475

View file

@ -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);