summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLee Susman <lsusman@codeaurora.org>2013-08-12 16:16:19 +0300
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-22 10:55:21 -0700
commitc92fad3700aa3f7fe1fb3d35ce9c5ae45192c792 (patch)
treeb4b01992b53225219029e0a4bff9f16efc309a53
parentb045711fdc9ea3fd6aad97f3e77684d079ab13da (diff)
scsi: ufs: add debugfs for ufs
Adding debugfs capability for ufshcd. This patch also implements command tag statistics enabling, disabling, printing and resetting. Change-Id: Idea0323391029c446a0a75aa5bea7070e3ab8125 Signed-off-by: Lee Susman <lsusman@codeaurora.org> [gbroner@codeaurora.org: fix minor merge conflict] Signed-off-by: Gilad Broner <gbroner@codeaurora.org> [subhashj@codeaurora.org: resolved trivial merge conflicts] Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
-rw-r--r--drivers/scsi/ufs/Makefile1
-rw-r--r--drivers/scsi/ufs/debugfs.c184
-rw-r--r--drivers/scsi/ufs/debugfs.h32
-rw-r--r--drivers/scsi/ufs/ufshcd.c26
-rw-r--r--drivers/scsi/ufs/ufshcd.h21
5 files changed, 264 insertions, 0 deletions
diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
index 0352e47b8397..9cddfaaa60a3 100644
--- a/drivers/scsi/ufs/Makefile
+++ b/drivers/scsi/ufs/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
obj-$(CONFIG_SCSI_UFS_TEST) += ufs_test.o
+obj-$(CONFIG_DEBUG_FS) += debugfs.o
diff --git a/drivers/scsi/ufs/debugfs.c b/drivers/scsi/ufs/debugfs.c
new file mode 100644
index 000000000000..5c515a0da3c8
--- /dev/null
+++ b/drivers/scsi/ufs/debugfs.c
@@ -0,0 +1,184 @@
+/* Copyright (c) 2013, 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * UFS debugfs - add debugfs interface to the ufshcd.
+ * This is currently used for statistics collection and exporting from the
+ * UFS driver.
+ * This infrastructure can be used for debugging or direct tweaking
+ * of the driver from userspace.
+ *
+ */
+
+#include "debugfs.h"
+
+static int ufsdbg_tag_stats_show(struct seq_file *file, void *data)
+{
+ struct ufs_hba *hba = (struct ufs_hba *)file->private;
+ struct ufs_stats *ufs_stats;
+ int i;
+ int max_depth;
+ bool is_tag_empty = true;
+
+ if (!hba)
+ goto exit;
+
+ ufs_stats = &hba->ufs_stats;
+
+ if (!ufs_stats->enabled) {
+ pr_debug("%s: ufs statistics are disabled\n", __func__);
+ goto exit;
+ }
+
+ max_depth = hba->nutrs;
+
+ mutex_lock(&ufs_stats->lock);
+
+ pr_debug("%s: UFS tag statistics:\n", __func__);
+ pr_debug("%s: Max tagged command queue depth is %d",
+ __func__, max_depth);
+
+ for (i = 0 ; i < max_depth ; ++i) {
+ if (hba->ufs_stats.tag_stats[i] != 0) {
+ is_tag_empty = false;
+ seq_printf(file,
+ "%s: Dispatched tag %d - %llu times\n",
+ __func__, i, ufs_stats->tag_stats[i]);
+
+ pr_debug("%s: Dispatched tag %d - %llu times\n",
+ __func__, i, ufs_stats->tag_stats[i]);
+ }
+ }
+ mutex_unlock(&ufs_stats->lock);
+
+ if (is_tag_empty)
+ pr_debug("%s: All tags statistics are empty", __func__);
+
+exit:
+ return 0;
+}
+
+static int ufsdbg_tag_stats_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ufsdbg_tag_stats_show, inode->i_private);
+}
+
+static ssize_t ufsdbg_tag_stats_write(struct file *filp,
+ const char __user *ubuf, size_t cnt,
+ loff_t *ppos)
+{
+ struct ufs_hba *hba = filp->f_mapping->host->i_private;
+ struct ufs_stats *ufs_stats;
+ int val = 0;
+ int ret;
+
+ ret = kstrtoint_from_user(ubuf, cnt, 0, &val);
+ if (ret) {
+ dev_err(hba->dev, "%s: Invalid argument\n", __func__);
+ return ret;
+ }
+
+ ufs_stats = &hba->ufs_stats;
+ mutex_lock(&ufs_stats->lock);
+
+ if (!val) {
+ ufs_stats->enabled = false;
+ pr_debug("%s: Disabling UFS tag statistics", __func__);
+ } else {
+ ufs_stats->enabled = true;
+ pr_debug("%s: Enabling & Resetting UFS tag statistics",
+ __func__);
+ memset(ufs_stats->tag_stats, 0,
+ sizeof(unsigned int) * hba->nutrs);
+ }
+
+ mutex_unlock(&ufs_stats->lock);
+ return cnt;
+}
+
+static const struct file_operations ufsdbg_tag_stats_fops = {
+ .open = ufsdbg_tag_stats_open,
+ .read = seq_read,
+ .write = ufsdbg_tag_stats_write,
+};
+
+static int ufshcd_init_tag_statistics(struct ufs_hba *hba)
+{
+ int ret = 0;
+
+ mutex_init(&hba->ufs_stats.lock);
+
+ hba->ufs_stats.tag_stats = kzalloc(hba->nutrs * sizeof(u64),
+ GFP_KERNEL);
+ if (!hba->ufs_stats.tag_stats) {
+ dev_err(hba->dev,
+ "%s: Unable to allocate UFS tag_stats", __func__);
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ hba->ufs_stats.enabled = false;
+
+exit:
+ return ret;
+}
+
+void ufsdbg_add_debugfs(struct ufs_hba *hba)
+{
+ if (!hba) {
+ dev_err(hba->dev, "%s: NULL hba, exiting", __func__);
+ goto err_no_root;
+ }
+
+ hba->debugfs_files.debugfs_root = debugfs_create_dir("ufs", NULL);
+ if (IS_ERR(hba->debugfs_files.debugfs_root))
+ /* Don't complain -- debugfs just isn't enabled */
+ goto err_no_root;
+ if (!hba->debugfs_files.debugfs_root) {
+ /*
+ * Complain -- debugfs is enabled, but it failed to
+ * create the directory
+ */
+ dev_err(hba->dev,
+ "%s: NULL debugfs root directory, exiting", __func__);
+ goto err_no_root;
+ }
+
+ hba->debugfs_files.tag_stats =
+ debugfs_create_file("tag_stats", S_IRUSR,
+ hba->debugfs_files.debugfs_root, hba,
+ &ufsdbg_tag_stats_fops);
+ if (!hba->debugfs_files.tag_stats) {
+ dev_err(hba->dev, "%s: NULL tag stats file, exiting",
+ __func__);
+ goto err;
+ }
+
+ if (ufshcd_init_tag_statistics(hba)) {
+ dev_err(hba->dev, "%s: Error initializing tag stats",
+ __func__);
+ goto err;
+ }
+
+ return;
+
+err:
+ debugfs_remove_recursive(hba->debugfs_files.debugfs_root);
+ hba->debugfs_files.debugfs_root = NULL;
+err_no_root:
+ dev_err(hba->dev, "%s: failed to initialize debugfs\n", __func__);
+}
+
+void ufsdbg_remove_debugfs(struct ufs_hba *hba)
+{
+ debugfs_remove_recursive(hba->debugfs_files.debugfs_root);
+ kfree(hba->ufs_stats.tag_stats);
+
+}
diff --git a/drivers/scsi/ufs/debugfs.h b/drivers/scsi/ufs/debugfs.h
new file mode 100644
index 000000000000..9e6cd5d34c4c
--- /dev/null
+++ b/drivers/scsi/ufs/debugfs.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2013, 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * UFS debugfs - add debugfs interface to the ufshcd.
+ * This is currently used for statistics collection and exporting from the
+ * UFS driver.
+ * This infrastructure can be used for debugging or direct tweaking
+ * of the driver from userspace.
+ *
+ */
+
+#ifndef _UFS_DEBUGFS_H
+#define _UFS_DEBUGFS_H
+
+#include <linux/debugfs.h>
+#include "ufshcd.h"
+
+#ifdef CONFIG_DEBUG_FS
+void ufsdbg_add_debugfs(struct ufs_hba *hba);
+
+void ufsdbg_remove_debugfs(struct ufs_hba *hba);
+#endif
+
+#endif /* End of Header */
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 65da78e0d3f7..ea4273c34999 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -42,6 +42,28 @@
#include "ufshcd.h"
#include "unipro.h"
+#include "debugfs.h"
+
+#ifdef CONFIG_DEBUG_FS
+#define UFSHCD_UPDATE_TAG_STATS(hba, tag) \
+ do { \
+ if (hba->ufs_stats.enabled) { \
+ mutex_lock(&hba->ufs_stats.lock); \
+ hba->ufs_stats.tag_stats[tag]++; \
+ mutex_unlock(&hba->ufs_stats.lock); \
+ } \
+ } while (0);
+
+#define UFSDBG_ADD_DEBUGFS(hba) ufsdbg_add_debugfs(hba);
+
+#define UFSDBG_REMOVE_DEBUGFS(hba) ufsdbg_remove_debugfs(hba);
+
+#else
+#define UFSHCD_UPDATE_TAG_STATS(hba, tag) do {} while (0);
+#define UFSDBG_ADD_DEBUGFS(hba) do {} while (0);
+#define UFSDBG_REMOVE_DEBUGFS(hba) do {} while (0);
+
+#endif
#define UFSHCD_ENABLE_INTRS (UTP_TRANSFER_REQ_COMPL |\
UTP_TASK_REQ_COMPL |\
@@ -802,6 +824,7 @@ void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
ufshcd_clk_scaling_start_busy(hba);
__set_bit(task_tag, &hba->outstanding_reqs);
ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL);
+ UFSHCD_UPDATE_TAG_STATS(hba, task_tag)
}
/**
@@ -5656,6 +5679,8 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
async_schedule(ufshcd_async_scan, hba);
+ UFSDBG_ADD_DEBUGFS(hba)
+
return 0;
out_remove_scsi_host:
@@ -5665,6 +5690,7 @@ exit_gating:
out_disable:
hba->is_irq_enabled = false;
scsi_host_put(host);
+ UFSDBG_REMOVE_DEBUGFS(hba)
ufshcd_hba_exit(hba);
out_error:
return err;
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 2570d9477b37..ebc0996735fc 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -333,6 +333,19 @@ struct ufs_init_prefetch {
u32 icc_level;
};
+#ifdef CONFIG_DEBUG_FS
+struct ufs_stats {
+ u64 *tag_stats;
+ struct mutex lock;
+ bool enabled;
+};
+
+struct debugfs_files {
+ struct dentry *debugfs_root;
+ struct dentry *tag_stats;
+};
+#endif
+
/**
* struct ufs_hba - per adapter private structure
* @mmio_base: UFSHCI base register address
@@ -378,6 +391,8 @@ struct ufs_init_prefetch {
* @dev_cmd: ufs device management command information
* @last_dme_cmd_tstamp: time stamp of the last completed DME command
* @auto_bkops_enabled: to track whether bkops is enabled in device
+ * @ufs_stats: ufshcd statistics to be used via debugfs
+ * @debugfs_files: debugfs files associated with the ufs stats
* @vreg_info: UFS device voltage regulator information
* @clk_list_head: UFS host controller clocks list node head
* @pwr_info: holds current power mode
@@ -503,6 +518,12 @@ struct ufs_hba {
/* Keeps information of the UFS device connected to this host */
struct ufs_dev_info dev_info;
bool auto_bkops_enabled;
+
+#ifdef CONFIG_DEBUG_FS
+ struct ufs_stats ufs_stats;
+ struct debugfs_files debugfs_files;
+#endif
+
struct ufs_vreg_info vreg_info;
struct list_head clk_list_head;