summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2010-06-17 21:11:18 -0700
committerGary King <gking@nvidia.com>2010-06-23 13:53:37 -0700
commitb4a85638135253daad20f9dba9a69a3228c51f59 (patch)
tree106d9d0e7674c9d76db1ed2463108f16cc823612
parent6efeb6ab2a7dad72f2763cd2fb7ce7e09563adea (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.c1
-rw-r--r--arch/arm/mach-tegra/board.h6
-rw-r--r--arch/arm/mach-tegra/cpuidle.c36
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap20/ap20rm_clock_config.c2
-rw-r--r--arch/arm/mach-tegra/nvrm/core/ap20/ap20rm_power_dfs.h4
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 */