[PATCH] clocks: add clock debugging file

Jeremy Kerr jeremy.kerr at canonical.com
Mon Nov 15 05:41:52 UTC 2010


Add a debugfs file to expose system clocks.

Signed-off-by: Jeremy Kerr <jeremy.kerr at canonical.com>

---
Yong: as promised, here's the sample debug code for the common struck clk

---
 arch/Kconfig             |    4 +
 arch/arm/common/clkdev.c |    2 
 include/linux/clk.h      |   20 ++++++++
 kernel/clk.c             |   92 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 118 insertions(+)

diff --git a/arch/Kconfig b/arch/Kconfig
index 212bd3c..8c2e329 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -168,6 +168,10 @@ config HAVE_USER_RETURN_NOTIFIER
 config USE_COMMON_STRUCT_CLK
 	bool
 
+config CLK_DEBUG
+	bool "Expose clock status information in debugfs"
+	depends on USE_COMMON_STRUCT_CLK && DEBUG_FS
+
 config HAVE_PERF_EVENTS_NMI
 	bool
 	help
diff --git a/arch/arm/common/clkdev.c b/arch/arm/common/clkdev.c
index e2b2bb6..7524445 100644
--- a/arch/arm/common/clkdev.c
+++ b/arch/arm/common/clkdev.c
@@ -97,6 +97,7 @@ void clkdev_add(struct clk_lookup *cl)
 {
 	mutex_lock(&clocks_mutex);
 	list_add_tail(&cl->node, &clocks);
+	clk_register(cl->clk);
 	mutex_unlock(&clocks_mutex);
 }
 EXPORT_SYMBOL(clkdev_add);
@@ -106,6 +107,7 @@ void __init clkdev_add_table(struct clk_lookup *cl, size_t num)
 	mutex_lock(&clocks_mutex);
 	while (num--) {
 		list_add_tail(&cl->node, &clocks);
+		clk_register(cl->clk);
 		cl++;
 	}
 	mutex_unlock(&clocks_mutex);
diff --git a/include/linux/clk.h b/include/linux/clk.h
index ae7e4ed..d6e64bf 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -20,6 +20,8 @@ struct device;
 
 #ifdef CONFIG_USE_COMMON_STRUCT_CLK
 
+#define CLK_NAME_LEN	32
+
 #define CLK_ATOMIC	0x1
 
 /* If we're using the common struct clk, we define the base clk object here */
@@ -64,14 +66,25 @@ struct clk {
 		struct mutex	mutex;
 		spinlock_t	spinlock;
 	} lock;
+#ifdef CONFIG_CLK_DEBUG
+	const char		name[CLK_NAME_LEN];
+	struct list_head	list;
+#endif /* CONFIG_CLK_DEBUG */
 };
 
+#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 */
@@ -80,6 +93,7 @@ struct clk {
 	.enable_count	= 0,						\
 	.flags		= CLK_ATOMIC,					\
 	.lock.spinlock	= __SPIN_LOCK_UNLOCKED(name.lock.spinlock),	\
+	__INIT_CLK_DEBUG(name)						\
 }
 
 /**
@@ -308,4 +322,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
+extern void clk_register(struct clk *clk);
+#else
+static inline void clk_register(struct clk *clk) { }
+#endif
+
 #endif
diff --git a/kernel/clk.c b/kernel/clk.c
index 2779abb..1969b0c 100644
--- a/kernel/clk.c
+++ b/kernel/clk.c
@@ -9,7 +9,11 @@
  */
 
 #include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/fs.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/seq_file.h>
 
 int clk_enable(struct clk *clk)
 {
@@ -112,3 +116,91 @@ struct clk_ops clk_fixed_ops = {
 	.get_rate = clk_fixed_get_rate,
 };
 EXPORT_SYMBOL_GPL(clk_fixed_ops);
+
+#ifdef CONFIG_CLK_DEBUG
+static LIST_HEAD(clocks);
+static DEFINE_MUTEX(clocks_lock);
+
+void clk_register(struct clk *clk)
+{
+	struct clk *c;
+
+	mutex_lock(&clocks_lock);
+
+	/* Look for duplicates. Since we're being called from clkdev_add,
+	 * we may see multiple clk_lookups for one clock, so seeing the same
+	 * clock is fine. However, warn if we see different clocks with the
+	 * same name */
+	list_for_each_entry(c, &clocks, list) {
+		if (c == clk)
+			goto out_unlock;
+
+		if (!strcmp(clk->name, c->name))
+			pr_warn("clock %s: duplicate name\n", clk->name);
+	}
+
+	list_add(&clk->list, &clocks);
+out_unlock:
+	mutex_unlock(&clocks_lock);
+}
+
+static void *clk_debug_seq_start(struct seq_file *m, loff_t *pos)
+{
+	mutex_lock(&clocks_lock);
+	return seq_list_start(&clocks, *pos);
+}
+
+static void *clk_debug_seq_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	return seq_list_next(v, &clocks, pos);
+}
+
+static void clk_debug_seq_stop(struct seq_file *m, void *v)
+{
+	mutex_unlock(&clocks_lock);
+}
+
+static int clk_debug_seq_show(struct seq_file *m, void *v)
+{
+	struct clk *parent, *clk = list_entry(v, struct clk, list);
+	const char *parent_name = "root";
+
+	parent = clk_get_parent(clk);
+	if (parent && !IS_ERR(parent))
+		parent_name = parent->name;
+
+	seq_printf(m, "%s [parent %s] usecount %d rate %lu\n",
+			clk->name, parent_name,
+			clk->enable_count, clk_get_rate(clk));
+	return 0;
+}
+
+static const struct seq_operations clk_debug_seq_ops = {
+	.start	= clk_debug_seq_start,
+	.next	= clk_debug_seq_next,
+	.stop	= clk_debug_seq_stop,
+	.show	= clk_debug_seq_show,
+};
+
+static int clk_debug_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &clk_debug_seq_ops);
+}
+
+static const struct file_operations clk_debug_file_ops = {
+	.open		= clk_debug_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+int clk_debug_init(void)
+{
+	debugfs_create_file("clocks", 0444, NULL, NULL,
+			&clk_debug_file_ops);
+	return 0;
+}
+
+late_initcall(clk_debug_init);
+
+#endif /* CONFIG_CLK_DEBUG */



More information about the linaro-dev mailing list