In that case, I keep both, but add a condition checking in clkdev_add. See below:
From b8e2babd1cc30ffc1ca5fe3cc6a542a18b0ec7fa Mon Sep 17 00:00:00 2001
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.
---
arch/arm/common/Kconfig | 7 ++++
arch/arm/common/clkdev.c | 28 ++++++++++++++
include/linux/clk.h | 13 +++++++
kernel/clk.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 138 insertions(+), 0 deletions(-)
diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig
index 0a34c81..c84eb90 100644
--- a/arch/arm/common/Kconfig
+++ b/arch/arm/common/Kconfig
@@ -41,3 +41,10 @@ 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
+ default n
+ help
+ export clk debug information to user space
diff --git a/arch/arm/common/clkdev.c b/arch/arm/common/clkdev.c
index 9e4c4d9..936684f 100644
--- a/arch/arm/common/clkdev.c
+++ b/arch/arm/common/clkdev.c
@@ -19,6 +19,9 @@
#include <linux/mutex.h>
#include <linux/clk.h>
#include <linux/slab.h>
+#ifdef CONFIG_CLK_DEBUG
+#include <linux/debugfs.h>
+#endif
#include <asm/clkdev.h>
@@ -104,6 +107,10 @@ EXPORT_SYMBOL(clk_put);
void clkdev_add(struct clk_lookup *cl)
{
+#ifdef CONFIG_CLK_DEBUG
+ if (debugfs_initialized())
+ clk_debug_register(cl->clk);
+#endif
mutex_lock(&clocks_mutex);
list_add_tail(&cl->node, &clocks);
mutex_unlock(&clocks_mutex);
@@ -114,6 +121,10 @@ void __init clkdev_add_table(struct clk_lookup *cl, size_t num)
{
mutex_lock(&clocks_mutex);
while (num--) {
+#ifdef CONFIG_CLK_DEBUG
+ if (debugfs_initialized())
+ clk_debug_register(cl->clk);
+#endif
list_add_tail(&cl->node, &clocks);
cl++;
}
@@ -186,3 +197,20 @@ void clkdev_drop(struct clk_lookup *cl)
kfree(cl);
}
EXPORT_SYMBOL(clkdev_drop);
+
+#ifdef CONFIG_CLK_DEBUG
+static int __init clk_debugfs_init(void)
+{
+ struct clk_lookup *cl;
+ int err;
+
+ list_for_each_entry(cl, &clocks, node) {
+ err = clk_debug_register(cl->clk);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+late_initcall(clk_debugfs_init);
+
+#endif
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 56416b7..5a0139c 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -44,16 +44,28 @@ struct device;
* registered with the arch-specific clock management code; the clock driver
* code does not need to handle these.
*/
+#define CLK_NAME_LEN 32
struct clk {
const struct clk_ops *ops;
unsigned int enable_count;
struct mutex mutex;
+#ifdef CONFIG_CLK_DEBUG
+ 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,5 @@ 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);
+int clk_debug_register(struct clk *clk);
#endif
diff --git a/kernel/clk.c b/kernel/clk.c
index 32f25ef..df4da68 100644
--- a/kernel/clk.c
+++ b/kernel/clk.c
@@ -12,6 +12,10 @@
#include <linux/mutex.h>
#include <linux/module.h>
+#ifdef CONFIG_CLK_DEBUG
+#include <linux/debugfs.h>
+#endif
+
int clk_enable(struct clk *clk)
{
int ret = 0;
@@ -113,3 +117,89 @@ 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;
+}
+
+int clk_debug_register(struct clk *clk)
+{
+ int err;
+ struct clk *pa;
+
+ pa = clk_get_parent(clk);
+
+ if (pa && !IS_ERR(pa) && !pa->dentry) {
+ err = clk_debug_register(pa);
+ if (err)
+ return err;
+ }
+
+ if (!clk->dentry) {
+ err = clk_debug_register_one(clk);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+#else /* defined CONFIG_CLK_DEBUG */
+inline int clk_debug_register(struct clk *clk) {}
+#endif
+EXPORT_SYMBOL_GPL(clk_debug_register);
--
1.7.0.4
Cheers
Yong
On Thu, Nov 18, 2010 at 2:00 PM, Jeremy Kerr
<jeremy.kerr@canonical.com> wrote:
Hi Yong,
> the time of clock registering (normally at board initialization or other
> early stage of boot up), debug fs system has not been initialized (happends
> in core_initcall). Therefore, it is better to leave it in a standalone
> function which will be called in late_initcall.
If you rely on discovering all clocks in an initcall, then you will miss any
clocks that are added after this stage (eg, from modules).
I'd suggest allowing clk_debug_register to be called at any time, then you
will not have any timing issues.
Regards,
Jeremy