From 15446111027151577b15a199ef0715cb6196453b Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 26 Apr 2012 10:31:15 +0200 Subject: clock: ux500: add debugfs support for powerdebug Signed-off-by: Vincent Guittot Signed-off-by: Philippe Langlais --- arch/arm/mach-ux500/clock-db8500.c | 4 + arch/arm/mach-ux500/clock.c | 166 +++++++++++++++++++++++++++++++++++++ arch/arm/mach-ux500/clock.h | 5 ++ 3 files changed, 175 insertions(+) diff --git a/arch/arm/mach-ux500/clock-db8500.c b/arch/arm/mach-ux500/clock-db8500.c index 98e3d73052f..d41a22dd041 100644 --- a/arch/arm/mach-ux500/clock-db8500.c +++ b/arch/arm/mach-ux500/clock-db8500.c @@ -1157,6 +1157,10 @@ int __init db8500_clk_init(void) ARRAY_SIZE(u8500_v2_sysclks)); clkdev_add_table(u8500_clocks, ARRAY_SIZE(u8500_clocks)); +#ifdef CONFIG_DEBUG_FS + clk_debugfs_add_table(u8500_v2_sysclks, ARRAY_SIZE(u8500_v2_sysclks)); + clk_debugfs_add_table(u8500_clocks, ARRAY_SIZE(u8500_clocks)); +#endif return 0; } diff --git a/arch/arm/mach-ux500/clock.c b/arch/arm/mach-ux500/clock.c index da17bcfdc45..b8bac2d6966 100644 --- a/arch/arm/mach-ux500/clock.c +++ b/arch/arm/mach-ux500/clock.c @@ -13,6 +13,12 @@ #include #include +#ifdef CONFIG_DEBUG_FS +#include +#include /* for copy_from_user */ +static LIST_HEAD(clk_list); +#endif + #include "clock.h" #include "prcc.h" @@ -503,6 +509,166 @@ static struct clk_lookup clk_smp_twd_lookup = { }; #endif +#ifdef CONFIG_DEBUG_FS +/* + * debugfs support to trace clock tree hierarchy and attributes with + * powerdebug + */ +static struct dentry *clk_debugfs_root; + +void __init clk_debugfs_add_table(struct clk_lookup *cl, size_t num) +{ + while (num--) { + /* Check that the clock has not been already registered */ + if (!(cl->clk->list.prev != cl->clk->list.next)) + list_add_tail(&cl->clk->list, &clk_list); + + cl++; + } +} + +static ssize_t usecount_dbg_read(struct file *file, char __user *buf, + size_t size, loff_t *off) +{ + struct clk *clk = file->f_dentry->d_inode->i_private; + char cusecount[128]; + unsigned int len; + + len = sprintf(cusecount, "%u\n", clk->enabled); + return simple_read_from_buffer(buf, size, off, cusecount, len); +} + +static ssize_t rate_dbg_read(struct file *file, char __user *buf, + size_t size, loff_t *off) +{ + struct clk *clk = file->f_dentry->d_inode->i_private; + char crate[128]; + unsigned int rate; + unsigned int len; + + rate = clk_get_rate(clk); + len = sprintf(crate, "%u\n", rate); + return simple_read_from_buffer(buf, size, off, crate, len); +} + +static const struct file_operations usecount_fops = { + .read = usecount_dbg_read, +}; + +static const struct file_operations set_rate_fops = { + .read = rate_dbg_read, +}; + +static struct dentry *clk_debugfs_register_dir(struct clk *c, + struct dentry *p_dentry) +{ + struct dentry *d, *clk_d; + const char *p = c->name; + + if (!p) + p = "BUG"; + + clk_d = debugfs_create_dir(p, p_dentry); + if (!clk_d) + return NULL; + + d = debugfs_create_file("usecount", S_IRUGO, + clk_d, c, &usecount_fops); + if (!d) + goto err_out; + d = debugfs_create_file("rate", S_IRUGO, + clk_d, c, &set_rate_fops); + if (!d) + goto err_out; + /* + * TODO : not currently available in ux500 + * d = debugfs_create_x32("flags", S_IRUGO, clk_d, (u32 *)&c->flags); + * if (!d) + * goto err_out; + */ + + return clk_d; + +err_out: + debugfs_remove_recursive(clk_d); + return NULL; +} + +static int clk_debugfs_register_one(struct clk *c) +{ + struct clk *pa = c->parent; + struct clk *bpa = c->bus_parent; + + if (!(bpa && !pa)) { + c->dent = clk_debugfs_register_dir(c, + pa ? pa->dent : clk_debugfs_root); + if (!c->dent) + return -ENOMEM; + } + + if (bpa) { + c->dent_bus = clk_debugfs_register_dir(c, + bpa->dent_bus ? bpa->dent_bus : bpa->dent); + if ((!c->dent_bus) && (c->dent)) { + debugfs_remove_recursive(c->dent); + c->dent = NULL; + return -ENOMEM; + } + } + return 0; +} + +static int clk_debugfs_register(struct clk *c) +{ + int err; + struct clk *pa = c->parent; + struct clk *bpa = c->bus_parent; + + if (pa && (!pa->dent && !pa->dent_bus)) { + err = clk_debugfs_register(pa); + if (err) + return err; + } + + if (bpa && (!bpa->dent && !bpa->dent_bus)) { + err = clk_debugfs_register(bpa); + if (err) + return err; + } + + if ((!c->dent) && (!c->dent_bus)) { + 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, &clk_list, list) { + 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); +#endif /* defined(CONFIG_DEBUG_FS) */ + int __init clk_init(void) { if (cpu_is_u8500()) { diff --git a/arch/arm/mach-ux500/clock.h b/arch/arm/mach-ux500/clock.h index f96f9986e4d..2403e51dc7f 100644 --- a/arch/arm/mach-ux500/clock.h +++ b/arch/arm/mach-ux500/clock.h @@ -51,6 +51,10 @@ struct clk { struct regulator *regulator; struct clk *clock; struct list_head list; +#if defined(CONFIG_DEBUG_FS) + struct dentry *dent; /* For visible tree hierarchy */ + struct dentry *dent_bus; /* For visible tree hierarchy */ +#endif }; /** @@ -156,6 +160,7 @@ int clk_set_rate_rec(struct clk *clk, unsigned long rate); #ifdef CONFIG_DEBUG_FS int dbx500_clk_debug_init(struct clk **clks, int num); +void clk_debugfs_add_table(struct clk_lookup *cl, size_t num); #else static inline int dbx500_clk_debug_init(struct clk **clks, int num) { -- cgit v1.2.3