Hi Jeremy,

This is latest one for review. I add some minor changes in.

Cheers
Yong

>From efe7fa8bea67f9bf532c0074a92d938e6d6f4c5d Mon Sep 17 00:00:00 2001
From: Yong Shen <yong.s...@linaro.org>
Date: Thu, 18 Nov 2010 14:54:49 +0800
Subject: [PATCH] export clock debug information to user space

create a tree-like directory structure in debugfs so user space
tools like powerdebug can generate readable clock information.
more functions tend to be add in, like individual clock enable/disable
by writing to this debug interface.

Signed-off-by: Yong Shen <yong.s...@linaro.org>
---
 arch/arm/common/Kconfig  |    6 ++
 arch/arm/common/clkdev.c |    3 +
 include/linux/clk.h      |   18 +++++++
 kernel/clk.c             |  121 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 148 insertions(+), 0 deletions(-)

diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig
index 0a34c81..13d7cf9 100644
--- a/arch/arm/common/Kconfig
+++ b/arch/arm/common/Kconfig
@@ -41,3 +41,9 @@ config SHARP_SCOOP
 config COMMON_CLKDEV
        bool
        select HAVE_CLK
+
+config CLK_DEBUG
+       bool "clock debug information export to user space"
+       depends on USE_COMMON_STRUCT_CLK && PM_DEBUG && DEBUG_FS
+       help
+         export clk debug information to user space
diff --git a/arch/arm/common/clkdev.c b/arch/arm/common/clkdev.c
index 9e4c4d9..1d08fb3 100644
--- a/arch/arm/common/clkdev.c
+++ b/arch/arm/common/clkdev.c
@@ -19,6 +19,7 @@
 #include <linux/mutex.h>
 #include <linux/clk.h>
 #include <linux/slab.h>
+#include <linux/debugfs.h>

 #include <asm/clkdev.h>

@@ -104,6 +105,7 @@ EXPORT_SYMBOL(clk_put);

 void clkdev_add(struct clk_lookup *cl)
 {
+       clk_debug_register(cl->clk);
        mutex_lock(&clocks_mutex);
        list_add_tail(&cl->node, &clocks);
        mutex_unlock(&clocks_mutex);
@@ -114,6 +116,7 @@ void __init clkdev_add_table(struct clk_lookup
*cl, size_t num)
 {
        mutex_lock(&clocks_mutex);
        while (num--) {
+               clk_debug_register(cl->clk);
                list_add_tail(&cl->node, &clocks);
                cl++;
        }
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 56416b7..4aaddea 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -48,12 +48,24 @@ struct clk {
        const struct clk_ops    *ops;
        unsigned int            enable_count;
        struct mutex            mutex;
+#ifdef CONFIG_CLK_DEBUG
+#define CLK_NAME_LEN 32
+       char name[CLK_NAME_LEN];
+       struct dentry           *dentry;
+#endif
 };

+#ifdef CONFIG_CLK_DEBUG
+#define __INIT_CLK_DEBUG(n) .name = #n,
+#else
+#define __INIT_CLK_DEBUG(n)
+#endif
+
 #define INIT_CLK(name, o) {                                    \
        .ops            = &o,                                   \
        .enable_count   = 0,                                    \
        .mutex          = __MUTEX_INITIALIZER(name.mutex),      \
+       __INIT_CLK_DEBUG(name)                          \
 }

 struct clk_ops {
@@ -245,4 +257,10 @@ struct clk *clk_get_sys(const char *dev_id, const
char *con_id);
 int clk_add_alias(const char *alias, const char *alias_dev_name, char *id,
                        struct device *dev);

+#ifdef CONFIG_CLK_DEBUG
+void clk_debug_register(struct clk *clk);
+#else
+static inline void clk_debug_register(struct clk *clk) {}
+#endif
+
 #endif
diff --git a/kernel/clk.c b/kernel/clk.c
index 32f25ef..7f8bea9 100644
--- a/kernel/clk.c
+++ b/kernel/clk.c
@@ -11,6 +11,8 @@
 #include <linux/clk.h>
 #include <linux/mutex.h>
 #include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>

 int clk_enable(struct clk *clk)
 {
@@ -113,3 +115,122 @@ struct clk_ops clk_fixed_ops = {
        .get_rate = clk_fixed_get_rate,
 };
 EXPORT_SYMBOL_GPL(clk_fixed_ops);
+
+#ifdef CONFIG_CLK_DEBUG
+/*
+ *     debugfs support to trace clock tree hierarchy and attributes
+ */
+static int clk_debug_rate_get(void *data, u64 *val)
+{
+       struct clk *clk = data;
+
+       *val = (u64)clk_get_rate(clk);
+       return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(clk_debug_rate_fops, clk_debug_rate_get, NULL,
+               "%llu\n");
+
+
+static struct dentry *clk_root;
+static int clk_debug_register_one(struct clk *clk)
+{
+       int err;
+       struct dentry *d, *child, *child_tmp;
+       struct clk *pa = clk_get_parent(clk);
+
+       if (pa && !IS_ERR(pa))
+               d = debugfs_create_dir(clk->name, pa->dentry);
+       else {
+               if (!clk_root)
+                       clk_root = debugfs_create_dir("clocks", NULL);
+               if (!clk_root)
+                       return -ENOMEM;
+               d = debugfs_create_dir(clk->name, clk_root);
+       }
+
+       if (!d)
+               return -ENOMEM;
+
+       clk->dentry = d;
+
+       d = debugfs_create_u32("enable_count", S_IRUGO, clk->dentry,
+                       (u32 *)&clk->enable_count);
+       if (!d) {
+               err = -ENOMEM;
+               goto err_out;
+       }
+
+       d = debugfs_create_file("rate", S_IRUGO, clk->dentry, (void *)clk,
+                       &clk_debug_rate_fops);
+       if (!d) {
+               err = -ENOMEM;
+               goto err_out;
+       }
+
+       return 0;
+
+err_out:
+       d = clk->dentry;
+       list_for_each_entry_safe(child, child_tmp, &d->d_subdirs, d_u.d_child)
+               debugfs_remove(child);
+       debugfs_remove(clk->dentry);
+       return err;
+}
+
+struct preinit_clk {
+       struct list_head list;
+       struct clk *clk;
+};
+static LIST_HEAD(preinit_clks);
+static DEFINE_MUTEX(preinit_lock);
+static int init_done;
+
+void clk_debug_register(struct clk *clk)
+{
+       int err;
+       struct clk *pa;
+
+       if (init_done) {
+               pa = clk_get_parent(clk);
+
+               if (pa && !IS_ERR(pa) && !pa->dentry)
+                       clk_debug_register(pa);
+
+               if (!clk->dentry) {
+                       err = clk_debug_register_one(clk);
+                       if (err)
+                               return;
+               }
+       } else {
+               struct preinit_clk *p;
+               mutex_lock(&preinit_lock);
+               p = kmalloc(sizeof(*p), GFP_KERNEL);
+               if (!p)
+                       goto unlock;
+               p->clk = clk;
+               list_add(&p->list, &preinit_clks);
+unlock:
+               mutex_unlock(&preinit_lock);
+       }
+}
+EXPORT_SYMBOL_GPL(clk_debug_register);
+
+static int __init clk_debugfs_init(void)
+{
+       struct preinit_clk *pclk, *tmp;
+
+       if (debugfs_initialized())
+               init_done = 1;
+
+       list_for_each_entry(pclk, &preinit_clks, list) {
+               clk_debug_register(pclk->clk);
+       }
+
+       list_for_each_entry_safe(pclk, tmp, &preinit_clks, list) {
+               list_del(&pclk->list);
+               kfree(pclk);
+       }
+       return 0;
+}
+late_initcall(clk_debugfs_init);
+#endif
-- 
1.7.0.4

_______________________________________________
linaro-dev mailing list
linaro-dev@lists.linaro.org
http://lists.linaro.org/mailman/listinfo/linaro-dev

Reply via email to