msm: ocmem: Add debugfs support

Add support for viewing ocmem allocations, zones,
zone specific statistics and power states via debugfs.

Change-Id: I7c3d4d4bccfcd7a1825023d860ce491ae2a7ef5f
Signed-off-by: Naveen Ramaraj <nramaraj@codeaurora.org>
diff --git a/arch/arm/mach-msm/ocmem.c b/arch/arm/mach-msm/ocmem.c
index 82fe2f8..51445aa 100644
--- a/arch/arm/mach-msm/ocmem.c
+++ b/arch/arm/mach-msm/ocmem.c
@@ -63,6 +63,27 @@
 	"other_os",
 };
 
+/* Must be in sync with enum ocmem_zstat_item */
+static const char *zstat_names[NR_OCMEM_ZSTAT_ITEMS] = {
+	"Allocation requests",
+	"Synchronous allocations",
+	"Ranged allocations",
+	"Asynchronous allocations",
+	"Allocation failures",
+	"Allocations grown",
+	"Allocations freed",
+	"Allocations shrunk",
+	"OCMEM maps",
+	"Map failures",
+	"OCMEM unmaps",
+	"Unmap failures",
+	"Transfers to OCMEM",
+	"Transfers to DDR",
+	"Transfer failures",
+	"Evictions",
+	"Restorations",
+};
+
 struct ocmem_quota_table {
 	const char *name;
 	int id;
@@ -135,6 +156,23 @@
 		return 0;
 }
 
+inline void inc_ocmem_stat(struct ocmem_zone *z,
+				enum ocmem_zstat_item item)
+{
+	if (!z)
+		return;
+	atomic_long_inc(&z->z_stat[item]);
+}
+
+inline unsigned long get_ocmem_stat(struct ocmem_zone *z,
+				enum ocmem_zstat_item item)
+{
+	if (!z)
+		return 0;
+	else
+		return atomic_long_read(&z->z_stat[item]);
+}
+
 static struct ocmem_plat_data *parse_static_config(struct platform_device *pdev)
 {
 	struct ocmem_plat_data *pdata = NULL;
@@ -473,6 +511,60 @@
 	return NULL;
 }
 
+static int ocmem_zones_show(struct seq_file *f, void *dummy)
+{
+	unsigned i = 0;
+	for (i = OCMEM_GRAPHICS; i < OCMEM_CLIENT_MAX; i++) {
+		struct ocmem_zone *z = get_zone(i);
+		if (z && z->active == true)
+			seq_printf(f, "zone %s\t:0x%08lx - 0x%08lx (%4ld KB)\n",
+				get_name(z->owner), z->z_start, z->z_end - 1,
+				(z->z_end - z->z_start)/SZ_1K);
+	}
+	return 0;
+}
+
+static int ocmem_zones_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ocmem_zones_show, inode->i_private);
+}
+
+static const struct file_operations zones_show_fops = {
+	.open = ocmem_zones_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release,
+};
+
+static int ocmem_stats_show(struct seq_file *f, void *dummy)
+{
+	unsigned i = 0;
+	unsigned j = 0;
+	for (i = OCMEM_GRAPHICS; i < OCMEM_CLIENT_MAX; i++) {
+		struct ocmem_zone *z = get_zone(i);
+		if (z && z->active == true) {
+			seq_printf(f, "zone %s:\n", get_name(z->owner));
+			for (j = 0 ; j < ARRAY_SIZE(zstat_names); j++) {
+				seq_printf(f, "\t %s: %lu\n", zstat_names[j],
+					get_ocmem_stat(z, j));
+			}
+		}
+	}
+	return 0;
+}
+
+static int ocmem_stats_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ocmem_stats_show, inode->i_private);
+}
+
+static const struct file_operations stats_show_fops = {
+	.open = ocmem_stats_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release,
+};
+
 static int ocmem_zone_init(struct platform_device *pdev)
 {
 
@@ -549,6 +641,8 @@
 			z_ops->allocate = allocate_head;
 			z_ops->free = free_head;
 		}
+		/* zap the counters */
+		memset(zone->z_stat, 0 , sizeof(zone->z_stat));
 		zone->active = true;
 		active_zones++;
 
@@ -557,7 +651,19 @@
 
 		pr_info(" zone %s\t: 0x%08lx - 0x%08lx (%4ld KB)\n",
 				client_names[part->id], zone->z_start,
-				zone->z_end, part->p_size/SZ_1K);
+				zone->z_end - 1, part->p_size/SZ_1K);
+	}
+
+	if (!debugfs_create_file("zones", S_IRUGO, pdata->debug_node,
+					NULL, &zones_show_fops)) {
+		dev_err(dev, "Unable to create debugfs node for zones\n");
+		return -EBUSY;
+	}
+
+	if (!debugfs_create_file("stats", S_IRUGO, pdata->debug_node,
+					NULL, &stats_show_fops)) {
+		dev_err(dev, "Unable to create debugfs node for stats\n");
+		return -EBUSY;
 	}
 
 	dev_dbg(dev, "Total active zones = %d\n", active_zones);
@@ -587,6 +693,27 @@
 	return 0;
 }
 
+static int __devinit ocmem_debugfs_init(struct platform_device *pdev)
+{
+	struct dentry *debug_dir = NULL;
+	struct ocmem_plat_data *pdata = platform_get_drvdata(pdev);
+
+	debug_dir = debugfs_create_dir("ocmem", NULL);
+	if (!debug_dir || IS_ERR(debug_dir)) {
+		pr_err("ocmem: Unable to create debugfs root\n");
+		return PTR_ERR(debug_dir);
+	}
+
+	pdata->debug_node =  debug_dir;
+	return 0;
+}
+
+static void __devexit ocmem_debugfs_exit(struct platform_device *pdev)
+{
+	struct ocmem_plat_data *pdata = platform_get_drvdata(pdev);
+	debugfs_remove_recursive(pdata->debug_node);
+}
+
 static int __devinit msm_ocmem_probe(struct platform_device *pdev)
 {
 	struct device   *dev = &pdev->dev;
@@ -635,6 +762,9 @@
 
 	platform_set_drvdata(pdev, ocmem_pdata);
 
+	if (ocmem_debugfs_init(pdev))
+		return -EBUSY;
+
 	if (ocmem_core_init(pdev))
 		return -EBUSY;
 
@@ -644,7 +774,7 @@
 	if (ocmem_notifier_init())
 		return -EBUSY;
 
-	if (ocmem_sched_init())
+	if (ocmem_sched_init(pdev))
 		return -EBUSY;
 
 	if (ocmem_rdm_init(pdev))
@@ -661,6 +791,7 @@
 
 static int __devexit msm_ocmem_remove(struct platform_device *pdev)
 {
+	ocmem_debugfs_exit(pdev);
 	return 0;
 }