From: Yong Shen yong.shen@linaro.org
Expose clock debug information to debugfs, which makes it easier for clock system debug by using tools like powerdebug developed by Linaro power management group. For long term, this can go into common clock framework, but so far it depends on the process of common clk API development. Once the common clk API is ready in upstream, the clock debug information will be based on it too. The implementation based on common clk API had also been finished and reviewed with Jeremy.
Signed-off-by: Yong Shen yong.shen@linaro.org --- arch/arm/mach-mx5/clock-mx51-mx53.c | 41 ++++++++++- arch/arm/plat-mxc/Kconfig | 6 ++ arch/arm/plat-mxc/clock.c | 121 ++++++++++++++++++++++++++++++++ arch/arm/plat-mxc/include/mach/clock.h | 11 +++ 4 files changed, 177 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-mx5/clock-mx51-mx53.c b/arch/arm/mach-mx5/clock-mx51-mx53.c index 785e1a3..81d5080 100644 --- a/arch/arm/mach-mx5/clock-mx51-mx53.c +++ b/arch/arm/mach-mx5/clock-mx51-mx53.c @@ -464,7 +464,14 @@ static int _clk_main_bus_set_parent(struct clk *clk, struct clk *parent) return 0; }
+#ifdef CONFIG_CLK_DEBUG +#define __INIT_CLK_DEBUG(n) .name = #n, +#else +#define __INIT_CLK_DEBUG(n) +#endif + static struct clk main_bus_clk = { + __INIT_CLK_DEBUG(main_bus_clk) .parent = &pll2_sw_clk, .set_parent = _clk_main_bus_set_parent, }; @@ -728,23 +735,28 @@ static unsigned long _clk_ddr_hf_get_rate(struct clk *clk)
/* External high frequency clock */ static struct clk ckih_clk = { + __INIT_CLK_DEBUG(ckih_clk) .get_rate = get_high_reference_clock_rate, };
static struct clk ckih2_clk = { + __INIT_CLK_DEBUG(ckih2_clk) .get_rate = get_ckih2_reference_clock_rate, };
static struct clk osc_clk = { + __INIT_CLK_DEBUG(osc_clk) .get_rate = get_oscillator_reference_clock_rate, };
/* External low frequency (32kHz) clock */ static struct clk ckil_clk = { + __INIT_CLK_DEBUG(ckil_clk) .get_rate = get_low_reference_clock_rate, };
static struct clk pll1_main_clk = { + __INIT_CLK_DEBUG(pll1_main_clk) .parent = &osc_clk, .get_rate = clk_pll_get_rate, .enable = _clk_pll_enable, @@ -762,6 +774,7 @@ static struct clk pll1_main_clk = {
/* PLL1 SW supplies to ARM core */ static struct clk pll1_sw_clk = { + __INIT_CLK_DEBUG(pll1_sw_clk) .parent = &pll1_main_clk, .set_parent = _clk_pll1_sw_set_parent, .get_rate = clk_pll1_sw_get_rate, @@ -769,6 +782,7 @@ static struct clk pll1_sw_clk = {
/* PLL2 SW supplies to AXI/AHB/IP buses */ static struct clk pll2_sw_clk = { + __INIT_CLK_DEBUG(pll2_sw_clk) .parent = &osc_clk, .get_rate = clk_pll_get_rate, .set_rate = _clk_pll_set_rate, @@ -779,6 +793,7 @@ static struct clk pll2_sw_clk = {
/* PLL3 SW supplies to serial clocks like USB, SSI, etc. */ static struct clk pll3_sw_clk = { + __INIT_CLK_DEBUG(pll3_sw_clk) .parent = &osc_clk, .set_rate = _clk_pll_set_rate, .get_rate = clk_pll_get_rate, @@ -788,6 +803,7 @@ static struct clk pll3_sw_clk = {
/* PLL4 SW supplies to LVDS Display Bridge(LDB) */ static struct clk mx53_pll4_sw_clk = { + __INIT_CLK_DEBUG(mx53_pll4_clk) .parent = &osc_clk, .set_rate = _clk_pll_set_rate, .enable = _clk_pll_enable, @@ -796,22 +812,26 @@ static struct clk mx53_pll4_sw_clk = {
/* Low-power Audio Playback Mode clock */ static struct clk lp_apm_clk = { + __INIT_CLK_DEBUG(lp_apm_clk) .parent = &osc_clk, .set_parent = _clk_lp_apm_set_parent, };
static struct clk periph_apm_clk = { + __INIT_CLK_DEBUG(periph_apm_clk) .parent = &pll1_sw_clk, .set_parent = _clk_periph_apm_set_parent, };
static struct clk cpu_clk = { + __INIT_CLK_DEBUG(cpu_clk) .parent = &pll1_sw_clk, .get_rate = clk_cpu_get_rate, .set_rate = clk_cpu_set_rate, };
static struct clk ahb_clk = { + __INIT_CLK_DEBUG(ahb_clk) .parent = &main_bus_clk, .get_rate = clk_ahb_get_rate, .set_rate = _clk_ahb_set_rate, @@ -819,6 +839,7 @@ static struct clk ahb_clk = { };
static struct clk iim_clk = { + __INIT_CLK_DEBUG(iim_clk) .parent = &ipg_clk, .enable_reg = MXC_CCM_CCGR0, .enable_shift = MXC_CCM_CCGRx_CG15_OFFSET, @@ -826,17 +847,20 @@ static struct clk iim_clk = {
/* Main IP interface clock for access to registers */ static struct clk ipg_clk = { + __INIT_CLK_DEBUG(ipg_clk) .parent = &ahb_clk, .get_rate = clk_ipg_get_rate, };
static struct clk ipg_perclk = { + __INIT_CLK_DEBUG(ipg_clk) .parent = &lp_apm_clk, .get_rate = clk_ipg_per_get_rate, .set_parent = _clk_ipg_per_set_parent, };
static struct clk ahb_max_clk = { + __INIT_CLK_DEBUG(ahb_max_clk) .parent = &ahb_clk, .enable_reg = MXC_CCM_CCGR0, .enable_shift = MXC_CCM_CCGRx_CG14_OFFSET, @@ -845,6 +869,7 @@ static struct clk ahb_max_clk = { };
static struct clk aips_tz1_clk = { + __INIT_CLK_DEBUG(aips_tz1_clk) .parent = &ahb_clk, .secondary = &ahb_max_clk, .enable_reg = MXC_CCM_CCGR0, @@ -854,6 +879,7 @@ static struct clk aips_tz1_clk = { };
static struct clk aips_tz2_clk = { + __INIT_CLK_DEBUG(aips_tz2_clk) .parent = &ahb_clk, .secondary = &ahb_max_clk, .enable_reg = MXC_CCM_CCGR0, @@ -863,11 +889,13 @@ static struct clk aips_tz2_clk = { };
static struct clk gpt_32k_clk = { + __INIT_CLK_DEBUG(gpt_32k_clk) .id = 0, .parent = &ckil_clk, };
static struct clk kpp_clk = { + __INIT_CLK_DEBUG(kpp_clk) .id = 0, };
@@ -876,6 +904,7 @@ static struct clk dummy_clk = { };
static struct clk emi_slow_clk = { + __INIT_CLK_DEBUG(emi_slow_clk) .parent = &pll2_sw_clk, .enable_reg = MXC_CCM_CCGR5, .enable_shift = MXC_CCM_CCGRx_CG8_OFFSET, @@ -989,6 +1018,7 @@ static struct clk mipi_hsp_clk = {
#define DEFINE_CLOCK_CCGR(name, i, er, es, pfx, p, s) \ static struct clk name = { \ + __INIT_CLK_DEBUG(name) \ .id = i, \ .enable_reg = er, \ .enable_shift = es, \ @@ -1004,6 +1034,7 @@ static struct clk mipi_hsp_clk = {
#define DEFINE_CLOCK_MAX(name, i, er, es, pfx, p, s) \ static struct clk name = { \ + __INIT_CLK_DEBUG(name) \ .id = i, \ .enable_reg = er, \ .enable_shift = es, \ @@ -1082,6 +1113,7 @@ CLK_GET_RATE(uart, 1, UART) CLK_SET_PARENT(uart, 1, UART)
static struct clk uart_root_clk = { + __INIT_CLK_DEBUG(uart_root_clk) .parent = &pll2_sw_clk, .get_rate = clk_uart_get_rate, .set_parent = clk_uart_set_parent, @@ -1092,6 +1124,7 @@ CLK_GET_RATE(usboh3, 1, USBOH3) CLK_SET_PARENT(usboh3, 1, USBOH3)
static struct clk usboh3_clk = { + __INIT_CLK_DEBUG(usboh3_clk) .parent = &pll2_sw_clk, .get_rate = clk_usboh3_get_rate, .set_parent = clk_usboh3_set_parent, @@ -1137,6 +1170,7 @@ CLK_GET_RATE(ecspi, 2, CSPI) CLK_SET_PARENT(ecspi, 1, CSPI)
static struct clk ecspi_main_clk = { + __INIT_CLK_DEBUG(ecspi_main_clk) .parent = &pll3_sw_clk, .get_rate = clk_ecspi_get_rate, .set_parent = clk_ecspi_set_parent, @@ -1152,7 +1186,8 @@ CLK_SET_PARENT(esdhc2, 1, ESDHC2_MSHC2) CLK_SET_RATE(esdhc2, 1, ESDHC2_MSHC2)
#define DEFINE_CLOCK_FULL(name, i, er, es, gr, sr, e, d, p, s) \ - static struct clk name = { \ +static struct clk name = { \ + __INIT_CLK_DEBUG(name) \ .id = i, \ .enable_reg = er, \ .enable_shift = es, \ @@ -1358,8 +1393,10 @@ int __init mx51_clocks_init(unsigned long ckil, unsigned long osc, ckih2_reference = ckih2; oscillator_reference = osc;
- for (i = 0; i < ARRAY_SIZE(mx51_lookups); i++) + for (i = 0; i < ARRAY_SIZE(mx51_lookups); i++) { clkdev_add(&mx51_lookups[i]); + clk_debug_register(mx51_lookups[i].clk); + }
clk_tree_init();
diff --git a/arch/arm/plat-mxc/Kconfig b/arch/arm/plat-mxc/Kconfig index 389f217..a97f733 100644 --- a/arch/arm/plat-mxc/Kconfig +++ b/arch/arm/plat-mxc/Kconfig @@ -119,4 +119,10 @@ config IRAM_ALLOC bool select GENERIC_ALLOCATOR
+config CLK_DEBUG + bool "clock debug information export to user space" + depends on PM_DEBUG && DEBUG_FS + default n + help + export clk debug information to user space endif diff --git a/arch/arm/plat-mxc/clock.c b/arch/arm/plat-mxc/clock.c index 2ed3ab1..190bce1 100644 --- a/arch/arm/plat-mxc/clock.c +++ b/arch/arm/plat-mxc/clock.c @@ -37,6 +37,8 @@ #include <linux/proc_fs.h> #include <linux/semaphore.h> #include <linux/string.h> +#include <linux/slab.h> +#include <linux/debugfs.h>
#include <mach/clock.h> #include <mach/hardware.h> @@ -244,3 +246,122 @@ unsigned long mxc_decode_pll(unsigned int reg_val, u32 freq)
return ll; } + +#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_root; +static int clk_debug_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 { + if (!clk_root) + clk_root = debugfs_create_dir("clock", NULL); + if (!clk_root) + return -ENOMEM; + d = debugfs_create_dir(clk->name, clk_root); + } + + if (!d) + return -ENOMEM; + + clk->dentry = d; + + d = debugfs_create_u32("enable_count", S_IRUGO, clk->dentry, + (u32 *)&clk->usecount); + 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; +} + +struct preinit_clk { + struct list_head list; + struct clk *clk; +}; +static LIST_HEAD(preinit_clks); +static DEFINE_MUTEX(preinit_lock); +static int init_done; + +void clk_debug_register(struct clk *clk) +{ + int err; + struct clk *pa; + + if (init_done) { + pa = clk_get_parent(clk); + + if (pa && !IS_ERR(pa) && !pa->dentry) + clk_debug_register(pa); + + if (!clk->dentry) { + err = clk_debug_register_one(clk); + if (err) + return; + } + } else { + struct preinit_clk *p; + mutex_lock(&preinit_lock); + p = kmalloc(sizeof(*p), GFP_KERNEL); + if (p) { + p->clk = clk; + list_add(&p->list, &preinit_clks); + } + mutex_unlock(&preinit_lock); + } +} +EXPORT_SYMBOL_GPL(clk_debug_register); + +static int __init clk_debugfs_init(void) +{ + struct preinit_clk *pclk, *tmp; + + init_done = 1; + + mutex_lock(&preinit_lock); + list_for_each_entry(pclk, &preinit_clks, list) { + clk_debug_register(pclk->clk); + } + + list_for_each_entry_safe(pclk, tmp, &preinit_clks, list) { + list_del(&pclk->list); + kfree(pclk); + } + mutex_unlock(&preinit_lock); + return 0; +} +late_initcall(clk_debugfs_init); +#endif diff --git a/arch/arm/plat-mxc/include/mach/clock.h b/arch/arm/plat-mxc/include/mach/clock.h index 753a598..fb0e0bd 100644 --- a/arch/arm/plat-mxc/include/mach/clock.h +++ b/arch/arm/plat-mxc/include/mach/clock.h @@ -23,9 +23,14 @@ #ifndef __ASSEMBLY__ #include <linux/list.h>
+#define CLK_NAME_LEN 32 struct module;
struct clk { +#ifdef CONFIG_CLK_DEBUG + char name[CLK_NAME_LEN]; + struct dentry *dentry; +#endif int id; /* Source clock this clk depends on */ struct clk *parent; @@ -62,5 +67,11 @@ void clk_unregister(struct clk *clk);
unsigned long mxc_decode_pll(unsigned int pll, u32 f_ref);
+#ifdef CONFIG_CLK_DEBUG +void clk_debug_register(struct clk *clk); +#else +static inline void clk_debug_register(struct clk *clk) {} +#endif + #endif /* __ASSEMBLY__ */ #endif /* __ASM_ARCH_MXC_CLOCK_H__ */
On Wed, 19 Jan 2011, yong.shen@linaro.org wrote:
From: Yong Shen yong.shen@linaro.org
Expose clock debug information to debugfs, which makes it easier for clock system debug by using tools like powerdebug developed by Linaro power management group. For long term, this can go into common clock framework, but so far it depends on the process of common clk API development. Once the common clk API is ready in upstream, the clock debug information will be based on it too. The implementation based on common clk API had also been finished and reviewed with Jeremy.
Signed-off-by: Yong Shen yong.shen@linaro.org
Merged into linux-linaro-2.6.37.
Please remember to push the final version upstream as well when appropriate.
Nicolas