g 28, 2010 at 3:38 AM, <vishwanath.sripa...@linaro.org> wrote: > From: Vishwanath BS <vishwanath.sripa...@linaro.org> > > This patch has instrumentation code for measuring latencies for > various CPUIdle C states for OMAP. Idea here is to capture the > timestamp at various phases of CPU Idle and then compute the sw > latency for various c states. For OMAP, 32k clock is chosen as > reference clock this as is an always on clock. wkup domain memory > (scratchpad memory) is used for storing timestamps. One can see the > worstcase latencies in below sysfs entries (after enabling > CONFIG_CPU_IDLE_PROF > in .config). This information can be used to correctly configure cpu idle > latencies for various C states after adding HW latencies for each of > these sw latencies. > /sys/devices/system/cpu/cpu0/cpuidle/state<n>/actual_latency > /sys/devices/system/cpu/cpu0/cpuidle/state<n>/sleep_latency > /sys/devices/system/cpu/cpu0/cpuidle/state<n>/wkup_latency > > THis patch is tested on OMAP ZOOM3 using kevin's pm branch. > > Signed-off-by: Vishwanath BS <vishwanath.sripa...@linaro.org> > Cc: linaro-...@lists.linaro.org > --- > arch/arm/mach-omap2/cpuidle34xx.c | 58 ++++++++++++++++-- > arch/arm/mach-omap2/pm.h | 5 ++ > arch/arm/mach-omap2/sleep34xx.S | 121 > +++++++++++++++++++++++++++++++++++++ > drivers/cpuidle/Kconfig | 5 ++ > drivers/cpuidle/sysfs.c | 16 +++++- > include/linux/cpuidle.h | 3 + > 6 files changed, 202 insertions(+), 6 deletions(-) > > diff --git a/arch/arm/mach-omap2/cpuidle34xx.c > b/arch/arm/mach-omap2/cpuidle34xx.c > index 3d3d035..398bef8 > --- a/arch/arm/mach-omap2/cpuidle34xx.c > +++ b/arch/arm/mach-omap2/cpuidle34xx.c > @@ -25,6 +25,7 @@ > #include <linux/sched.h> > #include <linux/cpuidle.h> > > +#include <linux/clk.h> > #include <plat/prcm.h> > #include <plat/irqs.h> > #include <plat/powerdomain.h> > @@ -86,6 +87,11 @@ static struct cpuidle_params cpuidle_params_table[] = { > {1, 10000, 30000, 300000}, > }; > > +#ifdef CONFIG_CPU_IDLE_PROF > +static struct clk *clk_32k; > +#define CONVERT_32K_USEC(lat) (lat * (USEC_PER_SEC/clk_get_rate(clk_32k))) > +#endif > + > static int omap3_idle_bm_check(void) > { > if (!omap3_can_sleep()) > @@ -115,21 +121,28 @@ static int _cpuidle_deny_idle(struct powerdomain *pwrdm, > * Called from the CPUidle framework to program the device to the > * specified target state selected by the governor. > */ > + > static int omap3_enter_idle(struct cpuidle_device *dev, > struct cpuidle_state *state) > { > struct omap3_processor_cx *cx = cpuidle_get_statedata(state); > struct timespec ts_preidle, ts_postidle, ts_idle; > u32 mpu_state = cx->mpu_state, core_state = cx->core_state; > +#ifdef CONFIG_CPU_IDLE_PROF > + int idle_time, latency; > + long sleep_time, wkup_time, total_sleep_time; > + long preidle_time, postidle_time; > +#endif > > current_cx_state = *cx; > > - /* Used to keep track of the total time in idle */ > - getnstimeofday(&ts_preidle); > - > local_irq_disable(); > local_fiq_disable(); > - > + /* Used to keep track of the total time in idle */ > + getnstimeofday(&ts_preidle); > +#ifdef CONFIG_CPU_IDLE_PROF > + preidle_time = omap3_sram_get_32k_tick(); > +#endif > pwrdm_set_next_pwrst(mpu_pd, mpu_state); > pwrdm_set_next_pwrst(core_pd, core_state); > > @@ -153,9 +166,39 @@ return_sleep_time: > getnstimeofday(&ts_postidle); > ts_idle = timespec_sub(ts_postidle, ts_preidle); > > +#ifdef CONFIG_CPU_IDLE_PROF > + postidle_time = omap3_sram_get_32k_tick(); > +#endif > local_irq_enable(); > local_fiq_enable(); > > +#ifdef CONFIG_CPU_IDLE_PROF > + sleep_time = omap3_sram_get_sleep_time(); > + wkup_time = omap3_sram_get_wkup_time(); > + > + /* take care of overflow */ > + if (postidle_time < preidle_time) > + postidle_time += (u32) 0xffffffff; > + if (wkup_time < sleep_time) > + wkup_time += (u32) 0xffffffff; > + > + idle_time = postidle_time - preidle_time; > + total_sleep_time = wkup_time - sleep_time; > + latency = idle_time - total_sleep_time; > + sleep_time = omap3_sram_get_sleep_time(); > + wkup_time = omap3_sram_get_wkup_time(); > + > + /* calculate average latency after ignoring sprious ones */ > + if ((total_sleep_time > 0) && (latency > state->actual_latency) > + && (latency >= 0)) { > + state->actual_latency = CONVERT_32K_USEC(latency); > + latency = (sleep_time - preidle_time); > + state->sleep_latency = CONVERT_32K_USEC(latency); > + latency = postidle_time - wkup_time; > + state->wkup_latency = CONVERT_32K_USEC(latency); > + } > +#endif > + > return ts_idle.tv_nsec / NSEC_PER_USEC + ts_idle.tv_sec * USEC_PER_SEC; > } > > @@ -423,7 +466,9 @@ int __init omap3_idle_init(void) > struct omap3_processor_cx *cx; > struct cpuidle_state *state; > struct cpuidle_device *dev; > - > +#ifdef CONFIG_CPU_IDLE_PROF > + static struct device dummy_device; > +#endif > mpu_pd = pwrdm_lookup("mpu_pwrdm"); > core_pd = pwrdm_lookup("core_pwrdm"); > > @@ -456,6 +501,9 @@ int __init omap3_idle_init(void) > > omap3_cpuidle_update_states(); > > +#ifdef CONFIG_CPU_IDLE_PROF > + clk_32k = clk_get(&dummy_device, "wkup_32k_fck"); > +#endif > if (cpuidle_register_device(dev)) { > printk(KERN_ERR "%s: CPUidle register device failed\n", > __func__); > diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h > index 3de6ece..e62e87d 100644 > --- a/arch/arm/mach-omap2/pm.h > +++ b/arch/arm/mach-omap2/pm.h > @@ -82,4 +82,9 @@ extern unsigned int save_secure_ram_context_sz; > extern unsigned int omap24xx_cpu_suspend_sz; > extern unsigned int omap34xx_cpu_suspend_sz; > > +#ifdef CONFIG_CPU_IDLE_PROF > +extern u32 omap3_sram_get_wkup_time(); > +extern u32 omap3_sram_get_sleep_time(); > +extern u32 omap3_sram_get_32k_tick(); > +#endif > #endif > diff --git a/arch/arm/mach-omap2/sleep34xx.S b/arch/arm/mach-omap2/sleep34xx.S > index d522cd7..8dec5ef 100644 > --- a/arch/arm/mach-omap2/sleep34xx.S > +++ b/arch/arm/mach-omap2/sleep34xx.S > @@ -59,6 +59,20 @@ > #define SDRC_DLLA_STATUS_V OMAP34XX_SDRC_REGADDR(SDRC_DLLA_STATUS) > #define SDRC_DLLA_CTRL_V OMAP34XX_SDRC_REGADDR(SDRC_DLLA_CTRL) > > +#define TIMER_32K_SYNC_P 0x48320010 > +#define TIMER_32K_SYNC OMAP2_L4_IO_ADDRESS(TIMER_32K_SYNC_P) > + > +#define SCRATCHPAD_SLEEP_TIME_OFFSET 0x9f8 > +#define SCRATCHPAD_WKUP_TIME_OFFSET 0x9fc > +#define SCRATCHPAD_SLEEP_TIME > OMAP343X_CTRL_REGADDR(SCRATCHPAD_SLEEP_TIME_OFFSET) > +#define SCRATCHPAD_WKUP_TIME > OMAP343X_CTRL_REGADDR(SCRATCHPAD_WKUP_TIME_OFFSET) > +#define SCRATCHPAD_WKUP_TIME_P OMAP343X_CTRL_BASE + > SCRATCHPAD_WKUP_TIME_OFFSET > + > +#define CM_ICLKEN_WKUP OMAP34XX_CM_REGADDR(WKUP_MOD, CM_ICLKEN) > +#define CM_ICLKEN_WKUP_P OMAP3430_CM_BASE + WKUP_MOD + CM_ICLKEN > +#define CM_IDLEST_WKUP OMAP34XX_CM_REGADDR(WKUP_MOD, CM_IDLEST) > +#define CM_IDLEST_WKUP_P OMAP3430_CM_BASE + WKUP_MOD + CM_IDLEST > + > .text > /* Function to aquire the semaphore in scratchpad */ > ENTRY(lock_scratchpad_sem) > @@ -183,7 +197,31 @@ api_params: > .word 0x4, 0x0, 0x0, 0x1, 0x1 > ENTRY(save_secure_ram_context_sz) > .word . - save_secure_ram_context > +#ifdef CONFIG_CPU_IDLE_PROF > +ENTRY(omap3_sram_get_wkup_time) > + stmfd sp!, {lr} @ save registers on stack > + ldr r0, wkup_time > + ldr r0, [r0] > + ldmfd sp!, {pc} @ restore regs and return > +ENTRY(omap3_sram_get_wkup_time_sz) > + .word . - omap3_sram_get_wkup_time > + > +ENTRY(omap3_sram_get_sleep_time) > + stmfd sp!, {lr} @ save registers on stack > + ldr r0, sleep_time > + ldr r0, [r0] > + ldmfd sp!, {pc} @ restore regs and return > +ENTRY(omap3_sram_get_sleep_time_sz) > + .word . - omap3_sram_get_sleep_time > > +ENTRY(omap3_sram_get_32k_tick) > + stmfd sp!, {lr} @ save registers on stack > + ldr r0, sync_32k_timer > + ldr r0, [r0] > + ldmfd sp!, {pc} @ restore regs and return > +ENTRY(omap3_sram_get_32k_tick_sz) > + .word . - omap3_sram_get_32k_tick > +#endif > /* > * Forces OMAP into idle state > * > @@ -207,6 +245,13 @@ loop: > cmp r1, #0x0 > /* If context save is required, do that and execute wfi */ > bne save_context_wfi > + > +#ifdef CONFIG_CPU_IDLE_PROF > + ldr r4, sync_32k_timer > + ldr r5, [r4] > + ldr r6, sleep_time > + str r5, [r6] > +#endif > /* Data memory barrier and Data sync barrier */ > mov r1, #0 > mcr p15, 0, r1, c7, c10, 4 > @@ -224,8 +269,25 @@ loop: > nop > nop > nop > +#ifdef CONFIG_CPU_IDLE_PROF > + ldr r4, iclken_wkup > + ldr r5, [r4] > + orr r5, r5, #0x4 > + str r5, [r4] > + ldr r4, idlest_wkup > +wait_idlest: > + ldr r5, [r4] > + and r5, r5, #0x4 > + cmp r5, #0x0 > + bne wait_idlest > + ldr r4, sync_32k_timer > + ldr r5, [r4] > + ldr r6, wkup_time > + str r5, [r6] > +#endif > bl wait_sdrc_ok > > + > ldmfd sp!, {r0-r12, pc} @ restore regs and return > restore_es3: > /*b restore_es3*/ @ Enable to debug restore code > @@ -247,6 +309,23 @@ copy_to_sram: > blx r1 > restore: > /* b restore*/ @ Enable to debug restore code > +#ifdef CONFIG_CPU_IDLE_PROF > + ldr r4, iclken_wkup_p > + ldr r5, [r4] > + orr r5, r5, #0x4 > + str r5, [r4] > + ldr r4, idlest_wkup_p > +wait_idlest1: > + ldr r5, [r4] > + and r5, r5, #0x4 > + cmp r5, #0x0 > + bne wait_idlest1 > + ldr r4, sync_32k_timer_p > + ldr r5, [r4] > + ldr r6, wkup_time_p > + str r5, [r6] > +#endif > + > /* Check what was the reason for mpu reset and store the reason in > r9*/ > /* 1 - Only L1 and logic lost */ > /* 2 - Only L2 lost - In this case, we wont be here */ > @@ -587,6 +666,12 @@ finished: > mcr p15, 2, r10, c0, c0, 0 > isb > skip_l2_inval: > +#ifdef CONFIG_CPU_IDLE_PROF > + ldr r4, sync_32k_timer > + ldr r5, [r4] > + ldr r6, sleep_time > + str r5, [r6] > +#endif > /* Data memory barrier and Data sync barrier */ > mov r1, #0 > mcr p15, 0, r1, c7, c10, 4 > @@ -603,6 +688,22 @@ skip_l2_inval: > nop > nop > nop > +#ifdef CONFIG_CPU_IDLE_PROF > + ldr r4, iclken_wkup > + ldr r5, [r4] > + orr r5, r5, #0x4 > + str r5, [r4] > + ldr r4, idlest_wkup > +wait_idlest2: > + ldr r5, [r4] > + and r5, r5, #0x4 > + cmp r5, #0x0 > + bne wait_idlest2 > + ldr r4, sync_32k_timer > + ldr r5, [r4] > + ldr r6, wkup_time > + str r5, [r6] > +#endif > bl wait_sdrc_ok > /* restore regs and return */ > ldmfd sp!, {r0-r12, pc} > @@ -668,5 +769,25 @@ cache_pred_disable_mask: > .word 0xFFFFE7FB > control_stat: > .word CONTROL_STAT > +#ifdef CONFIG_CPU_IDLE_PROF > +sync_32k_timer: > + .word TIMER_32K_SYNC > +sync_32k_timer_p: > + .word TIMER_32K_SYNC_P > +sleep_time: > + .word SCRATCHPAD_SLEEP_TIME > +wkup_time: > + .word SCRATCHPAD_WKUP_TIME > +wkup_time_p: > + .word SCRATCHPAD_WKUP_TIME_P > +iclken_wkup: > + .word CM_ICLKEN_WKUP > +iclken_wkup_p: > + .word CM_ICLKEN_WKUP_P > +idlest_wkup: > + .word CM_IDLEST_WKUP > +idlest_wkup_p: > + .word CM_IDLEST_WKUP_P > +#endif > ENTRY(omap34xx_cpu_suspend_sz) > .word . - omap34xx_cpu_suspend > diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig > index 7dbc4a8..147456d 100644 > --- a/drivers/cpuidle/Kconfig > +++ b/drivers/cpuidle/Kconfig > @@ -18,3 +18,8 @@ config CPU_IDLE_GOV_MENU > bool > depends on CPU_IDLE && NO_HZ > default y > + > +config CPU_IDLE_PROF > + bool
Should not this be something like bool "CPU idle profiling " so that this entry shows up in 'make menuconfig' ? and this should have dependency on ARCH_OMAP3 also ? > + depends on CPU_IDLE > + default n > diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c > index 0310ffa..a3e9db1 100644 > --- a/drivers/cpuidle/sysfs.c > +++ b/drivers/cpuidle/sysfs.c > @@ -249,6 +249,11 @@ define_show_state_ull_function(usage) > define_show_state_ull_function(time) > define_show_state_str_function(name) > define_show_state_str_function(desc) > +#ifdef CONFIG_CPU_IDLE_PROF > +define_show_state_function(actual_latency) > +define_show_state_function(sleep_latency) > +define_show_state_function(wkup_latency) > +#endif > > define_one_state_ro(name, show_state_name); > define_one_state_ro(desc, show_state_desc); > @@ -256,7 +261,11 @@ define_one_state_ro(latency, show_state_exit_latency); > define_one_state_ro(power, show_state_power_usage); > define_one_state_ro(usage, show_state_usage); > define_one_state_ro(time, show_state_time); > - > +#ifdef CONFIG_CPU_IDLE_PROF > +define_one_state_ro(actual_latency, show_state_actual_latency); > +define_one_state_ro(sleep_latency, show_state_sleep_latency); > +define_one_state_ro(wkup_latency, show_state_wkup_latency); > +#endif > static struct attribute *cpuidle_state_default_attrs[] = { > &attr_name.attr, > &attr_desc.attr, > @@ -264,6 +273,11 @@ static struct attribute *cpuidle_state_default_attrs[] = > { > &attr_power.attr, > &attr_usage.attr, > &attr_time.attr, > +#ifdef CONFIG_CPU_IDLE_PROF > + &attr_actual_latency.attr, > + &attr_sleep_latency.attr, > + &attr_wkup_latency.attr, > +#endif > NULL > }; > > diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h > index 55215cc..6474f6a 100644 > --- a/include/linux/cpuidle.h > +++ b/include/linux/cpuidle.h > @@ -43,6 +43,9 @@ struct cpuidle_state { > > int (*enter) (struct cpuidle_device *dev, > struct cpuidle_state *state); > +#ifdef CONFIG_CPU_IDLE_PROF > + u32 actual_latency, sleep_latency, wkup_latency; > +#endif > }; > > /* Idle State Flags */ > -- > 1.7.0.4 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-omap" in > the body of a message to majord...@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- Silesh -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html