ARM: 6681/1: SPEAr: add debugfs support to clk API
authorShiraz Hashim <shiraz.hashim@st.com>
Wed, 16 Feb 2011 06:40:40 +0000 (07:40 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Wed, 9 Mar 2011 09:49:45 +0000 (09:49 +0000)
Reviewed-by: Stanley Miao <stanley.miao@windriver.com>
Signed-off-by: Shiraz Hashim <shiraz.hashim@st.com>
Signed-off-by: Viresh Kumar <viresh.kumar@st.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/plat-spear/clock.c
arch/arm/plat-spear/include/plat/clock.h

index 7e7ab606dc494a6ff5e00cfdc1dedf8f4efc2455..bdbd7ec9cb6b2f0dc4f30c3bd798cf0ff9316d63 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <linux/bug.h>
 #include <linux/clk.h>
+#include <linux/debugfs.h>
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/list.h>
 
 static DEFINE_SPINLOCK(clocks_lock);
 static LIST_HEAD(root_clks);
+#ifdef CONFIG_DEBUG_FS
+static LIST_HEAD(clocks);
+#endif
 
 static void propagate_rate(struct clk *, int on_init);
+#ifdef CONFIG_DEBUG_FS
+static int clk_debugfs_reparent(struct clk *);
+#endif
 
 static int generic_clk_enable(struct clk *clk)
 {
@@ -96,6 +103,10 @@ static void clk_reparent(struct clk *clk, struct pclk_info *pclk_info)
 
        clk->pclk = pclk_info->pclk;
        spin_unlock_irqrestore(&clocks_lock, flags);
+
+#ifdef CONFIG_DEBUG_FS
+       clk_debugfs_reparent(clk);
+#endif
 }
 
 static void do_clk_disable(struct clk *clk)
@@ -336,6 +347,12 @@ void clk_register(struct clk_lookup *cl)
 
        spin_unlock_irqrestore(&clocks_lock, flags);
 
+       /* debugfs specific */
+#ifdef CONFIG_DEBUG_FS
+       list_add(&clk->node, &clocks);
+       clk->cl = cl;
+#endif
+
        /* add clock to arm clockdev framework */
        clkdev_add(cl);
 }
@@ -885,3 +902,102 @@ void recalc_root_clocks(void)
        }
        spin_unlock_irqrestore(&clocks_lock, flags);
 }
+
+#ifdef CONFIG_DEBUG_FS
+/*
+ *     debugfs support to trace clock tree hierarchy and attributes
+ */
+static struct dentry *clk_debugfs_root;
+static int clk_debugfs_register_one(struct clk *c)
+{
+       int err;
+       struct dentry *d, *child;
+       struct clk *pa = c->pclk;
+       char s[255];
+       char *p = s;
+
+       if (c) {
+               if (c->cl->con_id)
+                       p += sprintf(p, "%s", c->cl->con_id);
+               if (c->cl->dev_id)
+                       p += sprintf(p, "%s", c->cl->dev_id);
+       }
+       d = debugfs_create_dir(s, pa ? pa->dent : clk_debugfs_root);
+       if (!d)
+               return -ENOMEM;
+       c->dent = d;
+
+       d = debugfs_create_u32("usage_count", S_IRUGO, c->dent,
+                       (u32 *)&c->usage_count);
+       if (!d) {
+               err = -ENOMEM;
+               goto err_out;
+       }
+       d = debugfs_create_u32("rate", S_IRUGO, c->dent, (u32 *)&c->rate);
+       if (!d) {
+               err = -ENOMEM;
+               goto err_out;
+       }
+       d = debugfs_create_x32("flags", S_IRUGO, c->dent, (u32 *)&c->flags);
+       if (!d) {
+               err = -ENOMEM;
+               goto err_out;
+       }
+       return 0;
+
+err_out:
+       d = c->dent;
+       list_for_each_entry(child, &d->d_subdirs, d_u.d_child)
+               debugfs_remove(child);
+       debugfs_remove(c->dent);
+       return err;
+}
+
+static int clk_debugfs_register(struct clk *c)
+{
+       int err;
+       struct clk *pa = c->pclk;
+
+       if (pa && !pa->dent) {
+               err = clk_debugfs_register(pa);
+               if (err)
+                       return err;
+       }
+
+       if (!c->dent) {
+               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;
+
+       d = debugfs_create_dir("clock", NULL);
+       if (!d)
+               return -ENOMEM;
+       clk_debugfs_root = d;
+
+       list_for_each_entry(c, &clocks, node) {
+               err = clk_debugfs_register(c);
+               if (err)
+                       goto err_out;
+       }
+       return 0;
+err_out:
+       debugfs_remove_recursive(clk_debugfs_root);
+       return err;
+}
+late_initcall(clk_debugfs_init);
+
+static int clk_debugfs_reparent(struct clk *c)
+{
+       debugfs_remove(c->dent);
+       return clk_debugfs_register_one(c);
+}
+#endif /* CONFIG_DEBUG_FS */
index 5a601d830971c1d467f0135b9e8cf73ba2740202..2ae6606930a60a5fa4d301516f961ea30827da2c 100644 (file)
@@ -88,6 +88,9 @@ struct rate_config {
  * @children: list for childrens or this clock
  * @sibling: node for list of clocks having same parents
  * @private_data: clock specific private data
+ * @node: list to maintain clocks linearly
+ * @cl: clocklook up assoicated with this clock
+ * @dent: object for debugfs
  */
 struct clk {
        unsigned int usage_count;
@@ -109,6 +112,11 @@ struct clk {
        struct list_head children;
        struct list_head sibling;
        void *private_data;
+#ifdef CONFIG_DEBUG_FS
+       struct list_head node;
+       struct clk_lookup *cl;
+       struct dentry *dent;
+#endif
 };
 
 /* pll configuration structure */