Hi Jeremy,

Thanks a lot.
Yes, it's also nice to have a file containing all the clock information
which you have implemented in the email.
Since we expect more features like enable/disable clocks in the debugfs, we
also like to have tree-like debugfs for clock information.
Below is my draft implementation. Original implementation is from omap
platform clock code, and I adopted to common clock device.

diff --git a/arch/arm/common/clkdev.c b/arch/arm/common/clkdev.c
index 9e4c4d9..e7a629a 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>

@@ -186,3 +187,128 @@ void clkdev_drop(struct clk_lookup *cl)
  kfree(cl);
 }
 EXPORT_SYMBOL(clkdev_drop);
+
+#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS)
+/*
+ * debugfs support to trace clock tree hierarchy and attributes
+ */
+
+static int open_file(struct inode *inode, struct file *file)
+{
+        file->private_data = inode->i_private;
+        return 0;
+}
+
+static ssize_t read_file(struct file *file, char __user *user_buf,
+                                size_t count, loff_t *ppos)
+{
+ struct clk *clk = file->private_data;
+ unsigned long rate;
+ char buf[32];
+ ssize_t ret;
+
+ rate = clk_get_rate(clk);
+
+ ret = snprintf(buf, 32, "%d\n", rate);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ return ret;
+}
+
+static const struct file_operations rate_fops = {
+ .open = open_file,
+ .read = read_file,
+};
+
+static struct dentry *clk_debugfs_root;
+
+static int clk_debugfs_register_one(struct clk *clk)
+{
+ int err;
+ struct dentry *d, *child, *child_tmp;
+ struct clk *pa = clk_get_parent(clk);
+
+ if ((pa != ERR_PTR(-ENOSYS)) && pa)
+ d = debugfs_create_dir(clk ? clk->name : "null", pa->dentry);
+ else
+ d = debugfs_create_dir(clk ? clk->name : "null", clk_debugfs_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,
&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;
+}
+
+static int clk_debugfs_register(struct clk *clk)
+{
+ int err;
+ struct clk *pa;
+
+ if (clk == ERR_PTR(-ENOSYS))
+ return -1;
+
+ if (!clk)
+ return -1;
+
+ pa = clk_get_parent(clk);
+
+ if ((pa != ERR_PTR(-ENOSYS)) && pa && !pa->dentry) {
+ err = clk_debugfs_register(pa);
+ if (err)
+ return err;
+ }
+
+ if (!clk->dentry) {
+ err = clk_debugfs_register_one(clk);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+static int __init clk_debugfs_init(void)
+{
+ struct clk_lookup *cl;
+ struct dentry *d;
+ int err;
+
+ d = debugfs_create_dir("clock", NULL);
+ if (!d)
+ return -ENOMEM;
+ clk_debugfs_root = d;
+
+ list_for_each_entry(cl, &clocks, node) {
+ err = clk_debugfs_register(cl->clk);
+ if (err)
+ goto err_out;
+ }
+ return 0;
+err_out:
+ debugfs_remove_recursive(clk_debugfs_root);
+ return err;
+}
+late_initcall(clk_debugfs_init);
+
+#endif /* defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS) */
diff --git a/arch/arm/plat-mxc/clock-common.c
b/arch/arm/plat-mxc/clock-common.c
index 8911813..96e19a4 100644
--- a/arch/arm/plat-mxc/clock-common.c
+++ b/arch/arm/plat-mxc/clock-common.c
@@ -93,11 +93,14 @@ void clk_mxc_disable(struct clk *_clk)
 unsigned long clk_mxc_get_rate(struct clk *_clk)
 {
  struct clk_mxc *clk = to_clk_mxc(_clk);
-
+
  if (clk->get_rate)
  return clk->get_rate(clk);

- return clk_get_rate(clk->parent);
+ if (clk->parent)
+ return clk_get_rate(clk->parent);
+
+ return 0;
 }

 /* Round the requested clock rate to the nearest supported
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 56416b7..0dc3443 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -44,16 +44,22 @@ struct device;
  * registered with the arch-specific clock management code; the clock
driver
  * code does not need to handle these.
  */
+#define CLK_NAME_LEN 16
 struct clk {
  const struct clk_ops *ops;
  unsigned int enable_count;
  struct mutex mutex;
+ char name[CLK_NAME_LEN];
+#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS)
+       struct dentry           *dentry;
+#endif
 };

-#define INIT_CLK(name, o) { \
+#define INIT_CLK(clk_name, o) { \
  .ops = &o, \
  .enable_count = 0, \
- .mutex = __MUTEX_INITIALIZER(name.mutex), \
+ .mutex = __MUTEX_INITIALIZER(clk_name.mutex), \
+ .name = #clk_name, \
 }

 struct clk_ops {


Yong

On Mon, Nov 15, 2010 at 1:41 PM, Jeremy Kerr <jeremy.k...@canonical.com>wrote:

> Add a debugfs file to expose system clocks.
>
> Signed-off-by: Jeremy Kerr <jeremy.k...@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 */
>
_______________________________________________
linaro-dev mailing list
linaro-dev@lists.linaro.org
http://lists.linaro.org/mailman/listinfo/linaro-dev

Reply via email to