From b74cb799ffff6249589cd5d02111e468fb13d96a Mon Sep 17 00:00:00 2001
From: Vincent Guittot <vincent.guittot@stericsson.com>
Date: Fri, 8 Oct 2010 17:29:49 +0200
Subject: [PATCH] add debugfs support for powerdebug

---
 arch/arm/mach-ux500/clock.c |  145 ++++++++++++++++++++++++++++++++++++++----
 arch/arm/mach-ux500/clock.h |    4 +
 2 files changed, 135 insertions(+), 14 deletions(-)

diff --git a/arch/arm/mach-ux500/clock.c b/arch/arm/mach-ux500/clock.c
index b89be26..fb54622 100755
--- a/arch/arm/mach-ux500/clock.c
+++ b/arch/arm/mach-ux500/clock.c
@@ -801,10 +801,6 @@ int __init clk_init(void)
 	return 0;
 }
 
-#ifdef CONFIG_DEBUG_FS
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-
 static LIST_HEAD(clocks);
 static DEFINE_MUTEX(clocks_mutex);
 
@@ -1075,6 +1071,10 @@ static void clk_register(struct clk *clk)
 	mutex_unlock(&clocks_mutex);
 }
 
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
 /*
  * The following makes it possible to view the status (especially reference
  * count and reset status) for the clocks in the platform by looking into the
@@ -1166,19 +1166,136 @@ static const struct file_operations u8500_clocks_operations = {
 	.release        = seq_release,
 };
 
-static int __init init_clk_read_debugfs(void)
+/*
+ *	debugfs support to trace clock tree hierarchy and attributes with
+ *	powerdebug
+ */
+static struct dentry *clk_debugfs_root;
+
+static int clk_debugfs_register_one(struct clk *c)
+{
+	int err;
+	struct dentry *d, *child, *child_tmp;
+	struct clk *pap = c->parent_periph;
+	struct clk *pac = c->parent_cluster;
+	char s[255];
+	char *p = s;
+
+	if (c->name == NULL)
+		p += sprintf(p, "BUG");
+	else
+		p += sprintf(p, "%s", c->name);
+
+	d = debugfs_create_dir(s, pac ? pac->dent_cluster : clk_debugfs_root);
+	if (!d)
+		return -ENOMEM;
+	c->dent_cluster = d;
+
+	d = debugfs_create_u8("usecount", S_IRUGO, c->dent_cluster, (u8 *)&c->enabled);
+	if (!d) {
+		err = -ENOMEM;
+		goto err_out;
+	}
+	d = debugfs_create_u32("rate", S_IRUGO, c->dent_cluster, (u32 *)&c->rate);
+	if (!d) {
+		err = -ENOMEM;
+		goto err_out;
+	}
+#if 0
+	/* not currently available in ux500 */
+	d = debugfs_create_x32("flags", S_IRUGO, c->dent_cluster, (u32 *)&c->flags);
+	if (!d) {
+		err = -ENOMEM;
+		goto err_out;
+	}
+#endif
+
+	if(pap) {
+		d = debugfs_create_dir(s, pap->dent_periph ? pap->dent_periph : pap->dent_cluster);
+		if (!d) {
+			err = -ENOMEM;
+			goto err_out;
+		}
+		c->dent_periph = d;
+
+		d = debugfs_create_u8("usecount", S_IRUGO, c->dent_periph, (u8 *)&c->enabled);
+		if (!d) {
+			err = -ENOMEM;
+			goto err_out_cluster;
+		}
+		d = debugfs_create_u32("rate", S_IRUGO, c->dent_periph, (u32 *)&c->rate);
+		if (!d) {
+			err = -ENOMEM;
+			goto err_out_cluster;
+		}
+	}
+
+	return 0;
+err_out_cluster:
+	d = c->dent_periph;
+	list_for_each_entry_safe(child, child_tmp, &d->d_subdirs, d_u.d_child)
+		debugfs_remove(child);
+	debugfs_remove(c->dent_periph);
+
+err_out:
+	d = c->dent_cluster;
+	list_for_each_entry_safe(child, child_tmp, &d->d_subdirs, d_u.d_child)
+		debugfs_remove(child);
+	debugfs_remove(c->dent_cluster);
+	return err;
+}
+
+static int clk_debugfs_register(struct clk *c)
 {
+	int err;
+	struct clk *pap = c->parent_periph;
+	struct clk *pac = c->parent_cluster;
+
+	if (pap && !pap->dent_periph) {
+		err = clk_debugfs_register(pap);
+		if (err)
+			return err;
+	}
+	if (pac && !pac->dent_cluster) {
+		err = clk_debugfs_register(pac);
+		if (err)
+			return err;
+	}
+
+	if ((!c->dent_periph) && (!c->dent_cluster)) {
+		err = clk_debugfs_register_one(c);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
+static int __init clk_debugfs_init(void)
+{
+	struct clk *c;
+	struct dentry *d;
+	int err;
+
 	/* Expose a simple debugfs interface to view all clocks */
 	(void) debugfs_create_file("u8500_clocks", S_IFREG | S_IRUGO,
 				   NULL, NULL, &u8500_clocks_operations);
+
+	d = debugfs_create_dir("clock", NULL);
+	if (!d)
+		return -ENOMEM;
+	clk_debugfs_root = d;
+
+	list_for_each_entry(c, &clocks, list) {
+		err = clk_debugfs_register(c);
+		if (err)
+			goto err_out;
+	}
 	return 0;
+err_out:
+	debugfs_remove_recursive(clk_debugfs_root);
+	return err;
 }
-/*
- * This needs to come in after the arch_initcall() for the
- * overall clocks, because debugfs is not available until
- * the subsystems come up.
- */
-module_init(init_clk_read_debugfs);
-#else
-static void clk_register(struct clk *clk) { }
-#endif
+late_initcall(clk_debugfs_init);
+
+#endif /* defined(CONFIG_DEBUG_FS) */
+
diff --git a/arch/arm/mach-ux500/clock.h b/arch/arm/mach-ux500/clock.h
index db5fc10..c54bf35 100644
--- a/arch/arm/mach-ux500/clock.h
+++ b/arch/arm/mach-ux500/clock.h
@@ -118,6 +118,10 @@ struct clk {
 	struct clk		*parent_periph;
 	struct clk		*clk_src;
 	int		is_clk_src;
+#if defined(CONFIG_DEBUG_FS)
+	struct dentry		*dent_cluster;	/* For visible tree hierarchy */
+	struct dentry		*dent_periph;	/* For visible tree hierarchy */
+#endif
 };
 
 #define DEFINE_CLK_SRC_PLL(_name)		\
-- 
1.7.0.4

