diff options
Diffstat (limited to 'drivers/scsi')
-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 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); |