Currently auto-hibern8 is activated if host supports
auto-hibern8 capability. However no error handlings are existed thus
this feature is kind of risky.

If "Hibernate Enter" or "Hibernate Exit" fail happens
during auto-hibern8 flow, the corresponding interrupt
"UIC_HIBERNATE_ENTER" or "UIC_HIBERNATE_EXIT" shall be raised
according to UFS specification.

This patch adds auto-hibern8 error handlings:

- Monitor "Hibernate Enter" and "Hibernate Exit" interrupts after
  auto-hibern8 feature is activated.
- If fail happens, trigger error handlings just like "manual-hibernate"
  fail and use the same flow: Identify errors and schedule UFS error
  handler in ufshcd_check_errors(), and then do host reset and restore
  in UFS error handler.

Signed-off-by: Stanley Chu <stanley....@mediatek.com>
---
 drivers/scsi/ufs/ufshcd.c | 14 ++++++++++++++
 drivers/scsi/ufs/ufshcd.h | 13 +++++++++++++
 drivers/scsi/ufs/ufshci.h |  3 +++
 3 files changed, 30 insertions(+)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 1665820c22fd..e0e3930abc19 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -5254,6 +5254,7 @@ static void ufshcd_err_handler(struct work_struct *work)
                        goto skip_err_handling;
        }
        if ((hba->saved_err & INT_FATAL_ERRORS) ||
+           ufshcd_is_auto_hibern8_error(hba, hba->saved_err) ||
            ((hba->saved_err & UIC_ERROR) &&
            (hba->saved_uic_err & (UFSHCD_UIC_DL_PA_INIT_ERROR |
                                   UFSHCD_UIC_DL_NAC_RECEIVED_ERROR |
@@ -5431,6 +5432,15 @@ static void ufshcd_check_errors(struct ufs_hba *hba)
                        queue_eh_work = true;
        }
 
+       if (ufshcd_is_auto_hibern8_error(hba, hba->errors)) {
+               dev_err(hba->dev,
+                       "%s: Auto Hibern8 %s failed - status: 0x%08x, upmcrs: 
0x%08x\n",
+                       __func__, (hba->errors & UIC_HIBERNATE_ENTER) ?
+                       "Enter" : "Exit",
+                       hba->errors, ufshcd_get_upmcrs(hba));
+               queue_eh_work = true;
+       }
+
        if (queue_eh_work) {
                /*
                 * update the transfer error masks to sticky bits, let's do this
@@ -5493,6 +5503,10 @@ static void ufshcd_tmc_handler(struct ufs_hba *hba)
 static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
 {
        hba->errors = UFSHCD_ERROR_MASK & intr_status;
+
+       if (ufshcd_is_auto_hibern8_error(hba, intr_status))
+               hba->errors |= (UFSHCD_UIC_AH8_ERROR_MASK & intr_status);
+
        if (hba->errors)
                ufshcd_check_errors(hba);
 
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index ecfa898b9ccc..1bd9c8b61ed2 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -740,6 +740,19 @@ return true;
 #endif
 }
 
+static inline bool ufshcd_is_auto_hibern8_supported(struct ufs_hba *hba)
+{
+       return (hba->capabilities & MASK_AUTO_HIBERN8_SUPPORT);
+}
+
+static inline bool ufshcd_is_auto_hibern8_error(struct ufs_hba *hba,
+                                               u32 intr_mask)
+{
+       return (ufshcd_is_auto_hibern8_supported(hba) &&
+               !hba->uic_async_done &&
+               (intr_mask & UFSHCD_UIC_AH8_ERROR_MASK));
+}
+
 #define ufshcd_writel(hba, val, reg)   \
        writel((val), (hba)->mmio_base + (reg))
 #define ufshcd_readl(hba, reg) \
diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h
index 6fa889de5ee5..4bcb205f2077 100644
--- a/drivers/scsi/ufs/ufshci.h
+++ b/drivers/scsi/ufs/ufshci.h
@@ -148,6 +148,9 @@ enum {
                                UIC_HIBERNATE_EXIT |\
                                UIC_POWER_MODE)
 
+#define UFSHCD_UIC_AH8_ERROR_MASK      (UIC_HIBERNATE_ENTER |\
+                                       UIC_HIBERNATE_EXIT)
+
 #define UFSHCD_UIC_MASK                (UIC_COMMAND_COMPL | 
UFSHCD_UIC_PWR_MASK)
 
 #define UFSHCD_ERROR_MASK      (UIC_ERROR |\
-- 
2.18.0

Reply via email to