From: Vishwanath.sripathy@linaro.org
This patch has instrumentation code for measuring CPUIdle C states for OMAP. THis is still under development and not completely tested yet. 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.
Signed-off-by: Vishwanath BS vishwanath.sripathy@linaro.org --- diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c index 3d3d035..30120cb --- a/arch/arm/mach-omap2/cpuidle34xx.c +++ b/arch/arm/mach-omap2/cpuidle34xx.c @@ -25,6 +25,8 @@ #include <linux/sched.h> #include <linux/cpuidle.h>
+#include <linux/clk.h> +#include <linux/jiffies.h> #include <plat/prcm.h> #include <plat/irqs.h> #include <plat/powerdomain.h> @@ -33,6 +35,7 @@ #include <plat/serial.h>
#include "pm.h" +#include <plat/common.h>
#ifdef CONFIG_CPU_IDLE
@@ -86,6 +89,13 @@ 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))) +u32 omap3_sram_get_wkup_time(); +u32 omap3_sram_get_sleep_time(); +#endif + static int omap3_idle_bm_check(void) { if (!omap3_can_sleep()) @@ -115,21 +125,26 @@ 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; + int idle_time, latency; + long sleep_time, wkup_time, total_sleep_time, preidle_time, postidle_time;
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 = omap_32k_read(); +#endif pwrdm_set_next_pwrst(mpu_pd, mpu_state); pwrdm_set_next_pwrst(core_pd, core_state);
@@ -153,9 +168,27 @@ return_sleep_time: getnstimeofday(&ts_postidle); ts_idle = timespec_sub(ts_postidle, ts_preidle);
+#ifdef CONFIG_CPU_IDLE_PROF + postidle_time = omap_32k_read(); +#endif local_irq_enable(); local_fiq_enable();
+#ifdef CONFIG_CPU_IDLE_PROF + /* TODO: take care of overflow */ + idle_time = postidle_time - preidle_time; + sleep_time = omap3_sram_get_sleep_time(); + wkup_time = omap3_sram_get_wkup_time(); + + /* TODO: take care of overflow */ + total_sleep_time = wkup_time - sleep_time; + latency = idle_time - total_sleep_time; + /* TODO: calculate average */ + state->actual_latency = CONVERT_32K_USEC(latency); + state->sleep_latency = CONVERT_32K_USEC(sleep_time - preidle_time); + state->wkup_latency = CONVERT_32K_USEC(postidle_time - wkup_time); +#endif + return ts_idle.tv_nsec / NSEC_PER_USEC + ts_idle.tv_sec * USEC_PER_SEC; }
@@ -419,6 +452,7 @@ struct cpuidle_driver omap3_idle_driver = { */ int __init omap3_idle_init(void) { + static struct device dummy_device; int i, count = 0; struct omap3_processor_cx *cx; struct cpuidle_state *state; @@ -456,6 +490,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/sleep34xx.S b/arch/arm/mach-omap2/sleep34xx.S index d522cd7..448f1ba --- a/arch/arm/mach-omap2/sleep34xx.S +++ b/arch/arm/mach-omap2/sleep34xx.S @@ -58,6 +58,9 @@ #define SDRC_MANUAL_1_P (OMAP343X_SDRC_BASE + SDRC_MANUAL_1) #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 0xfa320010 +#define SCRATCHPAD_SLEEP_TIME 0xfa0029f8 +#define SCRATCHPAD_WKUP_TIME 0xfa0029fc
.text /* Function to aquire the semaphore in scratchpad */ @@ -184,6 +187,22 @@ api_params: ENTRY(save_secure_ram_context_sz) .word . - save_secure_ram_context
+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 + /* * Forces OMAP into idle state * @@ -207,6 +226,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 +250,15 @@ loop: nop nop nop +#ifdef CONFIG_CPU_IDLE_PROF + 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 @@ -587,6 +620,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 +642,13 @@ skip_l2_inval: nop nop nop + +#ifdef CONFIG_CPU_IDLE_PROF + 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 +714,14 @@ cache_pred_disable_mask: .word 0xFFFFE7FB control_stat: .word CONTROL_STAT +sync_32k_timer: + .word TIMER_32K_SYNC +sleep_time: + .word SCRATCHPAD_SLEEP_TIME +wkup_time: + .word SCRATCHPAD_WKUP_TIME + ENTRY(omap34xx_cpu_suspend_sz) .word . - omap34xx_cpu_suspend + + diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index 7dbc4a8..b8f1e07 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 + depends on CPU_IDLE + default y diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index 0310ffa..a3e9db1 --- 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 */