From: Yong Shen
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
---
arch/arm/common/Kconfig |6 ++
arch/arm/common/clkdev.c |3 +
include/linux/clk.h | 18 +++
kernel/clk.c | 124 ++
4 files changed, 151 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
#include
#include
+#include
#include
@@ -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 ae7e4ed..bf9396a 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -64,14 +64,26 @@ struct clk {
struct mutexmutex;
spinlock_t spinlock;
} lock;
+#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
+
/* static initialiser for non-atomic clocks */
#define INIT_CLK(name, o) {\
.ops= &o, \
.enable_count = 0,\
.flags = 0,\
.lock.mutex = __MUTEX_INITIALIZER(name.lock.mutex), \
+ __INIT_CLK_DEBUG(name) \
}
/* static initialiser for atomic clocks */
@@ -308,4 +320,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 2779abb..89996f3 100644
--- a/kernel/clk.c
+++ b/kernel/clk.c
@@ -10,6 +10,8 @@
#include
#include
+#include
+#include
int clk_enable(struct clk *clk)
{
@@ -112,3 +114,125 @@ 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, chi