As of now SCSI initiated error handling is broken because,
the reset APIs don't try to bring back the device initialized and
ready for further transfers.
In case of timeouts, the scsi error handler takes care of handling aborts
and resets. Improve the error handling in such scenario by resetting the
device and host and re-initializing them in proper manner.
Signed-off-by: Sujit Reddy Thumma sthu...@codeaurora.org
---
drivers/scsi/ufs/ufshcd.c | 446 +++--
drivers/scsi/ufs/ufshcd.h |2 +
2 files changed, 393 insertions(+), 55 deletions(-)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 6bfe927..2829a42 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -69,9 +69,15 @@ enum {
/* UFSHCD states */
enum {
- UFSHCD_STATE_OPERATIONAL,
UFSHCD_STATE_RESET,
UFSHCD_STATE_ERROR,
+ UFSHCD_STATE_OPERATIONAL,
+};
+
+/* UFSHCD error handling flags */
+enum {
+ UFSHCD_EH_HOST_RESET_PENDING = (1 0),
+ UFSHCD_EH_DEVICE_RESET_PENDING = (1 1),
};
/* Interrupt configuration options */
@@ -87,6 +93,22 @@ enum {
INT_AGGR_CONFIG,
};
+#define ufshcd_set_device_reset_pending(h) \
+ (h-eh_flags |= UFSHCD_EH_DEVICE_RESET_PENDING)
+#define ufshcd_set_host_reset_pending(h) \
+ (h-eh_flags |= UFSHCD_EH_HOST_RESET_PENDING)
+#define ufshcd_device_reset_pending(h) \
+ (h-eh_flags UFSHCD_EH_DEVICE_RESET_PENDING)
+#define ufshcd_host_reset_pending(h) \
+ (h-eh_flags UFSHCD_EH_HOST_RESET_PENDING)
+#define ufshcd_clear_device_reset_pending(h) \
+ (h-eh_flags = ~UFSHCD_EH_DEVICE_RESET_PENDING)
+#define ufshcd_clear_host_reset_pending(h) \
+ (h-eh_flags = ~UFSHCD_EH_HOST_RESET_PENDING)
+
+static void ufshcd_tmc_handler(struct ufs_hba *hba);
+static void ufshcd_async_scan(void *data, async_cookie_t cookie);
+
/*
* ufshcd_wait_for_register - wait for register value to change
* @hba - per-adapter interface
@@ -883,9 +905,22 @@ static int ufshcd_queuecommand(struct Scsi_Host *host,
struct scsi_cmnd *cmd)
tag = cmd-request-tag;
- if (hba-ufshcd_state != UFSHCD_STATE_OPERATIONAL) {
+ switch (hba-ufshcd_state) {
+ case UFSHCD_STATE_OPERATIONAL:
+ break;
+ case UFSHCD_STATE_RESET:
err = SCSI_MLQUEUE_HOST_BUSY;
goto out;
+ case UFSHCD_STATE_ERROR:
+ set_host_byte(cmd, DID_ERROR);
+ cmd-scsi_done(cmd);
+ goto out;
+ default:
+ dev_WARN_ONCE(hba-dev, 1, %s: invalid state %d\n,
+ __func__, hba-ufshcd_state);
+ set_host_byte(cmd, DID_BAD_TARGET);
+ cmd-scsi_done(cmd);
+ goto out;
}
/* acquire the tag to make sure device cmds don't use it */
@@ -1604,8 +1639,6 @@ static int ufshcd_make_hba_operational(struct ufs_hba
*hba)
if (hba-ufshcd_state == UFSHCD_STATE_RESET)
scsi_unblock_requests(hba-host);
- hba-ufshcd_state = UFSHCD_STATE_OPERATIONAL;
-
out:
return err;
}
@@ -2302,6 +2335,106 @@ out:
}
/**
+ * ufshcd_utrl_is_rsr_enabled - check if run-stop register is enabled
+ * @hba: per-adapter instance
+ */
+static bool ufshcd_utrl_is_rsr_enabled(struct ufs_hba *hba)
+{
+ return ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_LIST_RUN_STOP) 0x1;
+}
+
+/**
+ * ufshcd_utmrl_is_rsr_enabled - check if run-stop register is enabled
+ * @hba: per-adapter instance
+ */
+static bool ufshcd_utmrl_is_rsr_enabled(struct ufs_hba *hba)
+{
+ return ufshcd_readl(hba, REG_UTP_TASK_REQ_LIST_RUN_STOP) 0x1;
+}
+
+/**
+ * ufshcd_complete_pending_tasks - complete outstanding tasks
+ * @hba: per adapter instance
+ *
+ * Abort in-progress task management commands and wakeup
+ * waiting threads.
+ *
+ * Returns non-zero error value when failed to clear all the commands.
+ */
+static int ufshcd_complete_pending_tasks(struct ufs_hba *hba)
+{
+ u32 reg;
+ int err = 0;
+ unsigned long flags;
+
+ if (!hba-outstanding_tasks)
+ goto out;
+
+ /* Clear UTMRL only when run-stop is enabled */
+ if (ufshcd_utmrl_is_rsr_enabled(hba))
+ ufshcd_writel(hba, ~hba-outstanding_tasks,
+ REG_UTP_TASK_REQ_LIST_CLEAR);
+
+ /* poll for max. 1 sec to clear door bell register by h/w */
+ reg = ufshcd_wait_for_register(hba,
+ REG_UTP_TASK_REQ_DOOR_BELL,
+ hba-outstanding_tasks, 0, 1000, 1000);
+ if (reg hba-outstanding_tasks)
+ err = -ETIMEDOUT;
+
+ spin_lock_irqsave(hba-host-host_lock, flags);
+ /* complete commands that were cleared out */
+ ufshcd_tmc_handler(hba);
+ spin_unlock_irqrestore(hba-host-host_lock, flags);
+out:
+ if (err)
+ dev_err(hba-dev, %s: failed, still pending = 0x%.8x\n,
+