diff options
author | Alex Frid <afrid@nvidia.com> | 2010-06-17 21:11:18 -0700 |
---|---|---|
committer | Gary King <gking@nvidia.com> | 2010-06-23 13:53:37 -0700 |
commit | b4a85638135253daad20f9dba9a69a3228c51f59 (patch) | |
tree | 106d9d0e7674c9d76db1ed2463108f16cc823612 | |
parent | 6efeb6ab2a7dad72f2763cd2fb7ce7e09563adea (diff) |
[ARM/tegra] power: tuned cpu idle loop.
- Updated cpuidle driver parameters:
Determined LP2 state target_residency as a break even time balancing the
power cost of LP2 entry/exit (estimated via LP2 latency), and LP2_vs_LP3
power saving - this approach is consistent with governor interpretation
of residency. As a result the latency_to_residency factor reduced from
2.0 to 0.3. Included exit latency into idle time returned to governor to
avoid double subtraction (by platform code and governor code) - otherwise
under-reporting of LP2 time would skew downward governor correction
algorithm. Replaced hard coded power good time with platform data.
- Updated DVFS parameters
Set CPU minimum frequency to 216MHz - additional dynamic power will be
compensated by savings due to increased LP2 residency (216MHz is provided
by low power PLL, and still allows CPU to run at lowest possible voltage).
Changed maximum CPU/EMC frequency ratio to match new low corner settings.
Change-Id: Ic58d0dd628f51bc3ede61a83c87792fca4f0845b
Reviewed-on: http://git-master/r/2916
Tested-by: Aleksandr Frid <afrid@nvidia.com>
Reviewed-by: Gary King <gking@nvidia.com>
-rw-r--r-- | arch/arm/mach-tegra/board-nvodm.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/board.h | 6 | ||||
-rw-r--r-- | arch/arm/mach-tegra/cpuidle.c | 36 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvrm/core/ap20/ap20rm_clock_config.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/nvrm/core/ap20/ap20rm_power_dfs.h | 4 |
5 files changed, 34 insertions, 15 deletions
diff --git a/arch/arm/mach-tegra/board-nvodm.c b/arch/arm/mach-tegra/board-nvodm.c index bcaaef41864a..44a9863d8f37 100644 --- a/arch/arm/mach-tegra/board-nvodm.c +++ b/arch/arm/mach-tegra/board-nvodm.c @@ -1408,6 +1408,7 @@ static void __init tegra_setup_suspend(void) do_register: tegra_init_suspend(plat); + tegra_init_idle(plat); } void __init tegra_setup_nvodm(bool standard_i2c, bool standard_spi) diff --git a/arch/arm/mach-tegra/board.h b/arch/arm/mach-tegra/board.h index a5cf92369052..d3e4f7e67fc4 100644 --- a/arch/arm/mach-tegra/board.h +++ b/arch/arm/mach-tegra/board.h @@ -32,6 +32,12 @@ void __init tegra_init_irq(void); void __init tegra_init_clock(void); void __init tegra_init_suspend(struct tegra_suspend_platform_data *plat); +#ifdef CONFIG_CPU_IDLE +void __init tegra_init_idle(struct tegra_suspend_platform_data *plat); +#else +#define tegra_init_idle(plat) (0) +#endif + #ifdef CONFIG_CPU_FREQ int tegra_start_dvfsd(void); #else diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c index f2b98f496885..bd752459924b 100644 --- a/arch/arm/mach-tegra/cpuidle.c +++ b/arch/arm/mach-tegra/cpuidle.c @@ -33,7 +33,12 @@ #include <mach/iomap.h> #include <linux/suspend.h> -static unsigned int latency_factor __read_mostly = 2; +#include "power.h" + +#define LATENCY_FACTOR_SHIFT 8 + +static unsigned int latency_factor __read_mostly = 80; // factor ~ 0.3 +static unsigned int pwrgood_latency = 2000; static unsigned int system_is_suspending = 0; module_param(latency_factor, uint, 0644); @@ -56,6 +61,11 @@ static int lp2_supported = 0; #define CLK_RESET_CLK_MASK_ARM 0x44 +void __init tegra_init_idle(struct tegra_suspend_platform_data *plat) +{ + pwrgood_latency = plat->cpu_timer; +} + static int tegra_idle_enter_lp3(struct cpuidle_device *dev, struct cpuidle_state *state) { @@ -93,16 +103,17 @@ extern unsigned int tegra_suspend_lp2(unsigned int); static int tegra_idle_enter_lp2(struct cpuidle_device *dev, struct cpuidle_state *state) { - ktime_t enter, exit; - s64 request, us, latency; + ktime_t enter; + s64 request, us, latency, idle_us; unsigned long log_us; void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); unsigned int last_sample = (unsigned int)cpuidle_get_statedata(state); /* LP2 not possible when running in SMP mode */ smp_rmb(); + idle_us = state->exit_latency + state->target_residency; request = ktime_to_us(tick_nohz_get_sleep_length()); - if (!lp2_supported || request <= state->exit_latency || + if (!lp2_supported || request <= idle_us || system_is_suspending || (!tegra_nvrm_lp2_allowed())) { dev->last_state = &dev->states[0]; return tegra_idle_enter_lp3(dev, &dev->states[0]); @@ -112,19 +123,19 @@ static int tegra_idle_enter_lp2(struct cpuidle_device *dev, enter = ktime_get(); request -= state->exit_latency; us = tegra_suspend_lp2((unsigned int)max_t(s64, 200, request)); - exit = ktime_sub(ktime_get(), enter); - latency = ktime_to_us(exit) - us; - /* FIXME: un-hardcode the powergood timer latency */ - latency += 2000; + idle_us = ktime_to_us(ktime_sub(ktime_get(), enter)); + + latency = pwrgood_latency + idle_us - us; cpuidle_set_statedata(state, (void*)(unsigned int)(latency)); state->exit_latency = (12*latency + 4*last_sample) >> 4; + state->target_residency = (latency_factor*state->exit_latency) >> + LATENCY_FACTOR_SHIFT; log_us = (unsigned long)us + __raw_readl(pmc + PMC_SCRATCH_21); __raw_writel(log_us, pmc + PMC_SCRATCH_21); - state->target_residency = latency_factor*state->exit_latency; local_irq_enable(); - return (int)us; + return (int)idle_us; } static int tegra_idle_lp2_allowed(struct notifier_block *nfb, @@ -183,7 +194,7 @@ static int tegra_idle_enter(unsigned int cpu) snprintf(state->name, CPUIDLE_NAME_LEN, "LP3"); snprintf(state->desc, CPUIDLE_DESC_LEN, "CPU flow-controlled"); state->exit_latency = 10; - state->target_residency = 10; + state->target_residency = 0; state->power_usage = 600; state->flags = CPUIDLE_FLAG_SHALLOW | CPUIDLE_FLAG_TIME_VALID; state->enter = tegra_idle_enter_lp3; @@ -196,7 +207,8 @@ static int tegra_idle_enter(unsigned int cpu) snprintf(state->desc, CPUIDLE_DESC_LEN, "CPU power-gate"); state->exit_latency = 2500; - state->target_residency = state->exit_latency * latency_factor; + state->target_residency = (state->exit_latency * + latency_factor) >> LATENCY_FACTOR_SHIFT; state->power_usage = 0; state->flags = CPUIDLE_FLAG_BALANCED | CPUIDLE_FLAG_TIME_VALID; state->enter = tegra_idle_enter_lp2; diff --git a/arch/arm/mach-tegra/nvrm/core/ap20/ap20rm_clock_config.c b/arch/arm/mach-tegra/nvrm/core/ap20/ap20rm_clock_config.c index 76153ab71602..8a86fc2776aa 100644 --- a/arch/arm/mach-tegra/nvrm/core/ap20/ap20rm_clock_config.c +++ b/arch/arm/mach-tegra/nvrm/core/ap20/ap20rm_clock_config.c @@ -300,7 +300,7 @@ static NvRmFreqKHz Ap20CpuToEmcRatio(NvRmFreqKHz Emc2xKHz) 7, 10, 11, 13, 14, 15, 17, 18, 18, 19, 20, 21, 22, 22, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32 }; - #define CPU_TO_EMC_MAX_RATIO (10) + #define CPU_TO_EMC_MAX_RATIO (12) NvRmFreqKHz CpuKHz; NvRmFreqKHz CpuMaxKHz = NvRmPrivGetSocClockLimits(NvRmModuleID_Cpu)->MaxKHz; diff --git a/arch/arm/mach-tegra/nvrm/core/ap20/ap20rm_power_dfs.h b/arch/arm/mach-tegra/nvrm/core/ap20/ap20rm_power_dfs.h index 7b387a61c07f..8b5c9d4ded39 100644 --- a/arch/arm/mach-tegra/nvrm/core/ap20/ap20rm_power_dfs.h +++ b/arch/arm/mach-tegra/nvrm/core/ap20/ap20rm_power_dfs.h @@ -58,7 +58,7 @@ extern "C" */ #define NVRM_DFS_PARAM_CPU_AP20 \ NvRmFreqMaximum, /* Maximum domain frequency set to h/w limit */ \ - 40000, /* Minimum domain frequency 40 MHz */ \ + 216000, /* Minimum domain frequency 216 MHz */ \ 1000, /* Frequency change upper band 1 MHz */ \ 1000, /* Frequency change lower band 1 MHz */ \ { /* RT starvation control parameters */ \ @@ -71,7 +71,7 @@ extern "C" 255, /* Proportional frequency boost increase 255/256 ~ 100% */ \ 32, /* Proportional frequency boost decrease 32/256 ~ 12% */ \ },\ - 3, /* Relative adjustement of average freqiency 1/2^3 ~ 12% */ \ + 2, /* Relative adjustement of average freqiency 1/2^2 ~ 25% */ \ 1, /* Number of smaple intervals with NRT to trigger boost = 2 */ \ 1 /* NRT idle cycles threshold = 1 */ |