summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorSubhash Jadavani <subhashj@codeaurora.org>2016-06-10 12:33:18 -0700
committerKyle Yan <kyan@codeaurora.org>2016-06-24 15:05:01 -0700
commit73fa24503bf34047745bfd0602ec59f3ee2c74db (patch)
treeab5f539a06dd902ca213d872272483d5cee885df /drivers
parenta26ae43d87f17437319b0a2f5e30b9f79ad18950 (diff)
scsi: ufs-debugfs: add error state
This change adds support to allow user space query if low level UFS driver has encountered any error or not, this state can be read/cleared via debugfs. Change-Id: I867a4621315108aff17be852cfaadcfa945566a7 Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/scsi/ufs/ufs-debugfs.c47
-rw-r--r--drivers/scsi/ufs/ufs-debugfs.h6
-rw-r--r--drivers/scsi/ufs/ufshcd.c16
-rw-r--r--drivers/scsi/ufs/ufshcd.h2
4 files changed, 69 insertions, 2 deletions
diff --git a/drivers/scsi/ufs/ufs-debugfs.c b/drivers/scsi/ufs/ufs-debugfs.c
index 0f2f9bd91e02..f3b4b6c08571 100644
--- a/drivers/scsi/ufs/ufs-debugfs.c
+++ b/drivers/scsi/ufs/ufs-debugfs.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1433,6 +1433,41 @@ static const struct file_operations ufsdbg_reset_controller = {
.write = ufsdbg_reset_controller_write,
};
+static int ufsdbg_clear_err_state(void *data, u64 val)
+{
+ struct ufs_hba *hba = data;
+
+ if (!hba)
+ return -EINVAL;
+
+ /* clear the error state on any write attempt */
+ hba->debugfs_files.err_occurred = false;
+
+ return 0;
+}
+
+static int ufsdbg_read_err_state(void *data, u64 *val)
+{
+ struct ufs_hba *hba = data;
+
+ if (!hba)
+ return -EINVAL;
+
+ *val = hba->debugfs_files.err_occurred ? 1 : 0;
+
+ return 0;
+}
+
+void ufsdbg_set_err_state(struct ufs_hba *hba)
+{
+ hba->debugfs_files.err_occurred = true;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(ufsdbg_err_state,
+ ufsdbg_read_err_state,
+ ufsdbg_clear_err_state,
+ "%llu\n");
+
void ufsdbg_add_debugfs(struct ufs_hba *hba)
{
char root_name[sizeof("ufshcd00")];
@@ -1594,6 +1629,16 @@ void ufsdbg_add_debugfs(struct ufs_hba *hba)
goto err;
}
+ hba->debugfs_files.err_state =
+ debugfs_create_file("err_state", S_IRUSR | S_IWUSR,
+ hba->debugfs_files.debugfs_root, hba,
+ &ufsdbg_err_state);
+ if (!hba->debugfs_files.err_state) {
+ dev_err(hba->dev,
+ "%s: failed create err_state debugfs entry", __func__);
+ goto err;
+ }
+
ufsdbg_setup_fault_injection(hba);
ufshcd_vops_add_debugfs(hba, hba->debugfs_files.debugfs_root);
diff --git a/drivers/scsi/ufs/ufs-debugfs.h b/drivers/scsi/ufs/ufs-debugfs.h
index bf4d51ac8935..13848e8b72e0 100644
--- a/drivers/scsi/ufs/ufs-debugfs.h
+++ b/drivers/scsi/ufs/ufs-debugfs.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -37,6 +37,7 @@ void ufsdbg_add_debugfs(struct ufs_hba *hba);
void ufsdbg_remove_debugfs(struct ufs_hba *hba);
void ufsdbg_pr_buf_to_std(struct ufs_hba *hba, int offset, int num_regs,
char *str, void *priv);
+void ufsdbg_set_err_state(struct ufs_hba *hba);
#else
static inline void ufsdbg_add_debugfs(struct ufs_hba *hba)
{
@@ -48,6 +49,9 @@ static inline void ufsdbg_pr_buf_to_std(struct ufs_hba *hba, int offset,
int num_regs, char *str, void *priv)
{
}
+void ufsdbg_set_err_state(struct ufs_hba *hba)
+{
+}
#endif
#ifdef CONFIG_UFS_FAULT_INJECTION
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 40dcaa8f0eee..ad679e5d3f76 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -71,6 +71,7 @@ static int ufshcd_tag_req_type(struct request *rq)
static void ufshcd_update_error_stats(struct ufs_hba *hba, int type)
{
+ ufsdbg_set_err_state(hba);
if (type < UFS_ERR_MAX)
hba->ufs_stats.err_stats[type]++;
}
@@ -2143,6 +2144,9 @@ ufshcd_wait_for_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
else
ret = -ETIMEDOUT;
+ if (ret)
+ ufsdbg_set_err_state(hba);
+
spin_lock_irqsave(hba->host->host_lock, flags);
hba->active_uic_cmd = NULL;
spin_unlock_irqrestore(hba->host->host_lock, flags);
@@ -2842,6 +2846,9 @@ static int ufshcd_wait_for_dev_cmd(struct ufs_hba *hba,
ufshcd_outstanding_req_clear(hba, lrbp->task_tag);
}
+ if (err)
+ ufsdbg_set_err_state(hba);
+
return err;
}
@@ -3874,6 +3881,9 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd)
ret = (status != PWR_OK) ? status : -1;
}
out:
+ if (ret)
+ ufsdbg_set_err_state(hba);
+
ufshcd_save_tstamp_of_last_dme_cmd(hba);
spin_lock_irqsave(hba->host->host_lock, flags);
hba->active_uic_cmd = NULL;
@@ -4947,6 +4957,11 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
ocs == OCS_MISMATCH_DATA_BUF_SIZE);
ufshcd_print_trs(hba, 1 << lrbp->task_tag, print_prdt);
}
+
+ if ((host_byte(result) == DID_ERROR) ||
+ (host_byte(result) == DID_ABORT))
+ ufsdbg_set_err_state(hba);
+
return result;
}
@@ -5532,6 +5547,7 @@ static void ufshcd_err_handler(struct work_struct *work)
hba = container_of(work, struct ufs_hba, eh_work);
+ ufsdbg_set_err_state(hba);
pm_runtime_get_sync(hba->dev);
ufshcd_hold_all(hba);
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 1ccda45743d6..a4ee3726edb0 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -543,6 +543,8 @@ struct debugfs_files {
u32 dme_local_attr_id;
u32 dme_peer_attr_id;
struct dentry *reset_controller;
+ struct dentry *err_state;
+ bool err_occurred;
#ifdef CONFIG_UFS_FAULT_INJECTION
struct dentry *err_inj_scenario;
struct dentry *err_inj_stats;