Hi Jeremy, Thanks for those comments. I attached a formal patch below for further review, hope I catched all your points right. Du to that I am behind a firewall, my free internet access is problematic, so I send out patch review by copying it to email. After this patch, powerdebug (developed by Amit Arora in power management group of Linaro) generates information on imx51 platform like this:
bash-2.05b# /powerdebug -vcd Clock Tree : ********** / |-- kpp_clk.clk <flags=0x0:rate=0:usecount=0> |-- pll2_sw_clk.clk <flags=0x0:rate=665000000:usecount=0> | |-- main_bus_clk.clk <flags=0x0:rate=665000000:usecount=0> | | |-- ahb_clk.clk <flags=0x0:rate=133000000:usecount=0> | |-- usboh3_clk.clk <flags=0x0:rate=166250000:usecount=0> |-- hsi2c_clk.clk <flags=0x0:rate=66500000:usecount=0> |-- i2c2_clk.clk <flags=0x0:rate=66500000:usecount=0> |-- i2c1_clk.clk <flags=0x0:rate=66500000:usecount=0> |-- fec_clk.clk <flags=0x0:rate=66500000:usecount=0> |-- gpt_clk.clk <flags=0x0:rate=66500000:usecount=0> |-- uart3_clk.clk <flags=0x0:rate=66500000:usecount=0> |-- uart2_clk.clk <flags=0x0:rate=66500000:usecount=0> |-- uart1_clk.clk <flags=0x0:rate=66500000:usecount=0> Patch: >From 7bb4a43abf0d36a322a61a055f7e7aef18d242ab Mon Sep 17 00:00:00 2001 From: Yong Shen <yong.s...@linaro.org> Date: Tue, 16 Nov 2010 15:00:28 +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 debugfs. Signed-off-by: Yong Shen <yong.s...@linaro.org> --- arch/arm/common/Kconfig | 7 +++ arch/arm/common/clkdev.c | 106 ++++++++++++++++++++++++++++++++++++++ arch/arm/plat-mxc/clock-common.c | 5 ++- include/linux/clk.h | 18 ++++++- 4 files changed, 133 insertions(+), 3 deletions(-) diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig index 0a34c81..0300c95 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 COMMON_CLKDEV && 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..cf81e70 100644 --- a/arch/arm/common/clkdev.c +++ b/arch/arm/common/clkdev.c @@ -20,6 +20,10 @@ #include <linux/clk.h> #include <linux/slab.h> +#ifdef CONFIG_CLK_DEBUG +#include <linux/debugfs.h> +#endif + #include <asm/clkdev.h> #ifndef CONFIG_USE_COMMON_STRUCT_CLK @@ -186,3 +190,105 @@ void clkdev_drop(struct clk_lookup *cl) kfree(cl); } EXPORT_SYMBOL(clkdev_drop); + +#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_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 && !IS_ERR(pa)) + d = debugfs_create_dir(clk->name, pa->dentry); + else + d = debugfs_create_dir(clk->name, 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, + &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; +} + +static int clk_debugfs_register(struct clk *clk) +{ + int err; + struct clk *pa; + + pa = clk_get_parent(clk); + + if (pa && !IS_ERR(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("clocks", 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_CLK_DEBUG */ diff --git a/arch/arm/plat-mxc/clock-common.c b/arch/arm/plat-mxc/clock-common.c index 8911813..20d38a8 100644 --- a/arch/arm/plat-mxc/clock-common.c +++ b/arch/arm/plat-mxc/clock-common.c @@ -97,7 +97,10 @@ unsigned long clk_mxc_get_rate(struct clk *_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..1102c2c 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -44,17 +44,31 @@ 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; +#ifdef CONFIG_CLK_DEBUG + char name[CLK_NAME_LEN]; + struct dentry *dentry; +#endif }; -#define INIT_CLK(name, o) { \ +#ifndef CONFIG_CLK_DEBUG +#define INIT_CLK(clk_name, o) { \ .ops = &o, \ .enable_count = 0, \ - .mutex = __MUTEX_INITIALIZER(name.mutex), \ + .mutex = __MUTEX_INITIALIZER(clk_name.mutex), \ } +#else +#define INIT_CLK(clk_name, o) { \ + .ops = &o, \ + .enable_count = 0, \ + .mutex = __MUTEX_INITIALIZER(clk_name.mutex), \ + .name = #clk_name, \ +} +#endif struct clk_ops { int (*enable)(struct clk *); -- 1.7.0.4 Thanks Yong On Mon, Nov 15, 2010 at 5:05 PM, Jeremy Kerr <jeremy.k...@canonical.com>wrote: > Hi Yong, > > > 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. > > Sure; if you want the extended functionality, I think your tree approach is > best. > > > Below is my draft implementation. Original implementation is from omap > > platform clock code, and I adopted to common clock device. > > A couple of comments inline... > > > 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> > > Minor nitpick: no need to put this #inlude on its own. > > > > > #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) > > Might be good to add CONFIG_CLK_DEBUG (which would depend on > CONFIG_PM_DEBUG > and CONFIG_DEBUG_FS), so we can enable/disable it separately. > > > +/* > > + * debugfs support to trace clock tree hierarchy and attributes > > + */ > > + > > +static int open_file(struct inode *inode, struct file *file) > > I think you can replace this (and read_file, and rate_fops) with something > like: > > 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, "%lu\n"); > > and then in clk_debugfs_register_one: > > debugfs_create_file("rate", S_IRUGO, clk->dentry, > clk, &clk_debug_rate_fops); > > > > +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) > > I'd suggest: > > if (pa && !IS_ERR(pa)) > > > + d = debugfs_create_dir(clk ? clk->name : "null", pa->dentry); > > + else > > + d = debugfs_create_dir(clk ? clk->name : "null", clk_debugfs_root); > > Why do you need to check for clk == NULL here? We probably don't want > 'null' > directories hanging around. > > > + > > + 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; > > Do we need these checks? > > > + > > + pa = clk_get_parent(clk); > > + > > + if ((pa != ERR_PTR(-ENOSYS)) && pa && !pa->dentry) { > > Again, this might be better: > > if (pa && !IS_ERR(pa) && !pa->dentry) > > .. but this makes me think we should probably drop the -ENOSYS return code > from clk_get_parent, but that's a patch for another time.. :) > > > + 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); > > I'd suggest "clocks" instead of "clock", but this is fairly minor. > > > > 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; > > I see you found this oops too :) > > > > 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 > > Probably best to put 'name' into the CLK_DEBUG case; then you'll just need > to > use the __INIT_CLK_DEBUG macros in my previous patch. > > BTW, the spacing in the patch seems to be a little off; not sure if this is > due to your mailer or the patch itself. If it's the latter, you might want > to > clean this up before submitting. > > Cheers, > > > Jeremy >
_______________________________________________ linaro-dev mailing list linaro-dev@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-dev