diff options
author | Todd Poynor <toddpoynor@google.com> | 2010-12-17 19:36:23 -0800 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:36:50 -0800 |
commit | 81dd0074b7d32a441e31c04d0517b095114f9875 (patch) | |
tree | e34138cc015891cb559874e57fc2b8a52bc71c67 /arch | |
parent | 76f6b96694672b6ef8a68fbee5b21ce9ec933107 (diff) |
ARM: tegra: Make CPU thermal throttling configurable
Based on work by Dmitriy Gruzman and Varun Wadekar.
Change-Id: I64d765628223b7ef1ec493b9e409ea11e9391b94
Signed-off-by: Todd Poynor <toddpoynor@google.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-tegra/Kconfig | 7 | ||||
-rw-r--r-- | arch/arm/mach-tegra/cpu-tegra.c | 266 |
2 files changed, 148 insertions, 125 deletions
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index da1d392e8f32..053b4cc2541e 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -132,3 +132,10 @@ config TEGRA_IOVMM config TEGRA_ARB_SEMAPHORE bool + +config TEGRA_THERMAL_THROTTLE + bool "Enable throttling of CPU speed on overtemp" + depends on CPU_FREQ + default y + help + Also requires enabling a temperature sensor such as NCT1008. diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c index fcf54ede8542..8e86009b4ac1 100644 --- a/arch/arm/mach-tegra/cpu-tegra.c +++ b/arch/arm/mach-tegra/cpu-tegra.c @@ -48,141 +48,32 @@ static struct cpufreq_frequency_table freq_table[] = { { 8, CPUFREQ_TABLE_END }, }; -/* CPU frequency is gradually lowered when throttling is enabled */ -#define THROTTLE_START_INDEX 2 -#define THROTTLE_END_INDEX 6 -#define THROTTLE_DELAY msecs_to_jiffies(2000) -#define NO_DELAY msecs_to_jiffies(0) - #define NUM_CPUS 2 static struct clk *cpu_clk; static struct clk *emc_clk; -static struct workqueue_struct *workqueue; - static unsigned long target_cpu_speed[NUM_CPUS]; static DEFINE_MUTEX(tegra_cpu_lock); static bool is_suspended; -static DEFINE_MUTEX(throttling_lock); -static bool is_throttling; -static struct delayed_work throttle_work; - - -int tegra_verify_speed(struct cpufreq_policy *policy) -{ - return cpufreq_frequency_table_verify(policy, freq_table); -} - -unsigned int tegra_getspeed(unsigned int cpu) -{ - unsigned long rate; - - if (cpu >= NUM_CPUS) - return 0; - - rate = clk_get_rate(cpu_clk) / 1000; - return rate; -} - -static int tegra_update_cpu_speed(unsigned long rate) -{ - int ret = 0; - struct cpufreq_freqs freqs; - - freqs.old = tegra_getspeed(0); - freqs.new = rate; - - if (freqs.old == freqs.new) - return ret; - - /* - * Vote on memory bus frequency based on cpu frequency - * This sets the minimum frequency, display or avp may request higher - */ - if (rate >= 816000) - clk_set_rate(emc_clk, 600000000); /* cpu 816 MHz, emc max */ - else if (rate >= 456000) - clk_set_rate(emc_clk, 300000000); /* cpu 456 MHz, emc 150Mhz */ - else - clk_set_rate(emc_clk, 100000000); /* emc 50Mhz */ - - for_each_online_cpu(freqs.cpu) - cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); - -#ifdef CONFIG_CPU_FREQ_DEBUG - printk(KERN_DEBUG "cpufreq-tegra: transition: %u --> %u\n", - freqs.old, freqs.new); -#endif - - ret = clk_set_rate(cpu_clk, freqs.new * 1000); - if (ret) { - pr_err("cpu-tegra: Failed to set cpu frequency to %d kHz\n", - freqs.new); - return ret; - } - - for_each_online_cpu(freqs.cpu) - cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); - - return 0; -} - -static unsigned long tegra_cpu_highest_speed(void) -{ - unsigned long rate = 0; - int i; - - for_each_online_cpu(i) - rate = max(rate, target_cpu_speed[i]); - return rate; -} - -static int tegra_target(struct cpufreq_policy *policy, - unsigned int target_freq, - unsigned int relation) -{ - int idx; - unsigned int freq; - unsigned int highest_speed; - unsigned int limit_when_throttling; - int ret = 0; - - mutex_lock(&tegra_cpu_lock); - - if (is_suspended) { - ret = -EBUSY; - goto out; - } - - cpufreq_frequency_table_target(policy, freq_table, target_freq, - relation, &idx); - - freq = freq_table[idx].frequency; +unsigned int tegra_getspeed(unsigned int cpu); +static int tegra_update_cpu_speed(unsigned long rate); - target_cpu_speed[policy->cpu] = freq; +/* CPU frequency is gradually lowered when throttling is enabled */ +#define THROTTLE_START_INDEX 2 +#define THROTTLE_END_INDEX 6 - highest_speed = tegra_cpu_highest_speed(); +#ifdef CONFIG_TEGRA_THERMAL_THROTTLE +#define THROTTLE_DELAY msecs_to_jiffies(2000) +#define NO_DELAY msecs_to_jiffies(0) - /* Do not go above this frequency when throttling */ - limit_when_throttling = freq_table[THROTTLE_START_INDEX].frequency; - - if (is_throttling && highest_speed > limit_when_throttling) { - if (tegra_getspeed(0) < limit_when_throttling) { - ret = tegra_update_cpu_speed(limit_when_throttling); - goto out; - } else { - ret = -EBUSY; - goto out; - } - } +static DEFINE_MUTEX(throttling_lock); +static bool is_throttling; +static struct delayed_work throttle_work; +static struct workqueue_struct *workqueue; - ret = tegra_update_cpu_speed(highest_speed); -out: - mutex_unlock(&tegra_cpu_lock); - return ret; -} +#define tegra_cpu_is_throttling() (is_throttling) static bool tegra_throttling_needed(unsigned long *rate) { @@ -214,7 +105,7 @@ static void tegra_throttle_work_func(struct work_struct *work) /** * tegra_throttling_enable - * This functions may sleep + * This function may sleep */ void tegra_throttling_enable(void) { @@ -231,7 +122,7 @@ EXPORT_SYMBOL_GPL(tegra_throttling_enable); /** * tegra_throttling_disable - * This functions may sleep + * This function may sleep */ void tegra_throttling_disable(void) { @@ -292,8 +183,129 @@ static void __exit tegra_cpu_debug_exit(void) late_initcall(tegra_cpu_debug_init); module_exit(tegra_cpu_debug_exit); +#endif /* CONFIG_DEBUG_FS */ + +#else /* CONFIG_TEGRA_THERMAL_THROTTLE */ +#define tegra_cpu_is_throttling() (0) +#endif /* CONFIG_TEGRA_THERMAL_THROTTLE */ + +int tegra_verify_speed(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, freq_table); +} + +unsigned int tegra_getspeed(unsigned int cpu) +{ + unsigned long rate; + + if (cpu >= NUM_CPUS) + return 0; + + rate = clk_get_rate(cpu_clk) / 1000; + return rate; +} + +static int tegra_update_cpu_speed(unsigned long rate) +{ + int ret = 0; + struct cpufreq_freqs freqs; + + freqs.old = tegra_getspeed(0); + freqs.new = rate; + + if (freqs.old == freqs.new) + return ret; + + /* + * Vote on memory bus frequency based on cpu frequency + * This sets the minimum frequency, display or avp may request higher + */ + if (rate >= 816000) + clk_set_rate(emc_clk, 600000000); /* cpu 816 MHz, emc max */ + else if (rate >= 456000) + clk_set_rate(emc_clk, 300000000); /* cpu 456 MHz, emc 150Mhz */ + else + clk_set_rate(emc_clk, 100000000); /* emc 50Mhz */ + + for_each_online_cpu(freqs.cpu) + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + +#ifdef CONFIG_CPU_FREQ_DEBUG + printk(KERN_DEBUG "cpufreq-tegra: transition: %u --> %u\n", + freqs.old, freqs.new); #endif + ret = clk_set_rate(cpu_clk, freqs.new * 1000); + if (ret) { + pr_err("cpu-tegra: Failed to set cpu frequency to %d kHz\n", + freqs.new); + return ret; + } + + for_each_online_cpu(freqs.cpu) + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return 0; +} + +static unsigned long tegra_cpu_highest_speed(void) +{ + unsigned long rate = 0; + int i; + + for_each_online_cpu(i) + rate = max(rate, target_cpu_speed[i]); + return rate; +} + +static int tegra_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + int idx; + unsigned int freq; + unsigned int new_speed; + int ret = 0; + + mutex_lock(&tegra_cpu_lock); + + if (is_suspended) { + ret = -EBUSY; + goto out; + } + + cpufreq_frequency_table_target(policy, freq_table, target_freq, + relation, &idx); + + freq = freq_table[idx].frequency; + + target_cpu_speed[policy->cpu] = freq; + + new_speed = tegra_cpu_highest_speed(); + + /* Do not go above this frequency when throttling */ + + if (tegra_cpu_is_throttling()) { + unsigned int throttle_limit = + freq_table[THROTTLE_START_INDEX].frequency; + + if (new_speed > throttle_limit) { + if (tegra_getspeed(0) < throttle_limit) { + new_speed = throttle_limit; + } else { + ret = -EBUSY; + goto out; + } + } + } + + ret = tegra_update_cpu_speed(new_speed); +out: + mutex_unlock(&tegra_cpu_lock); + return ret; +} + + static int tegra_pm_notify(struct notifier_block *nb, unsigned long event, void *dummy) { @@ -345,7 +357,6 @@ static int tegra_cpu_init(struct cpufreq_policy *policy) cpumask_copy(policy->related_cpus, cpu_possible_mask); if (policy->cpu == 0) { - INIT_DELAYED_WORK(&throttle_work, tegra_throttle_work_func); register_pm_notifier(&tegra_cpu_pm_notifier); } @@ -378,15 +389,20 @@ static struct cpufreq_driver tegra_cpufreq_driver = { static int __init tegra_cpufreq_init(void) { +#ifdef CONFIG_TEGRA_THERMAL_THROTTLE workqueue = create_singlethread_workqueue("cpu-tegra"); if (!workqueue) return -ENOMEM; + INIT_DELAYED_WORK(&throttle_work, tegra_throttle_work_func); +#endif return cpufreq_register_driver(&tegra_cpufreq_driver); } static void __exit tegra_cpufreq_exit(void) { +#ifdef CONFIG_TEGRA_THERMAL_THROTTLE destroy_workqueue(workqueue); +#endif cpufreq_unregister_driver(&tegra_cpufreq_driver); } |