From: Todd Poynor Date: Sat, 18 Dec 2010 03:36:23 +0000 (-0800) Subject: ARM: tegra: Make CPU thermal throttling configurable X-Git-Tag: firefly_0821_release~9833^2~79^2~3 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=fca172ff3d27904db22a2608910b9632de226ae2;p=firefly-linux-kernel-4.4.55.git ARM: tegra: Make CPU thermal throttling configurable Based on work by Dmitriy Gruzman and Varun Wadekar. Change-Id: I64d765628223b7ef1ec493b9e409ea11e9391b94 Signed-off-by: Todd Poynor --- diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index d351811d9551..983297dfa25e 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -96,3 +96,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 d8c103e8964c..eddd1b7fabf4 100644 --- a/arch/arm/mach-tegra/cpu-tegra.c +++ b/arch/arm/mach-tegra/cpu-tegra.c @@ -50,27 +50,146 @@ 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; +unsigned int tegra_getspeed(unsigned int cpu); +static int tegra_update_cpu_speed(unsigned long rate); + +/* CPU frequency is gradually lowered when throttling is enabled */ +#define THROTTLE_START_INDEX 2 +#define THROTTLE_END_INDEX 6 + +#ifdef CONFIG_TEGRA_THERMAL_THROTTLE +#define THROTTLE_DELAY msecs_to_jiffies(2000) +#define NO_DELAY msecs_to_jiffies(0) + static DEFINE_MUTEX(throttling_lock); static bool is_throttling; static struct delayed_work throttle_work; +static struct workqueue_struct *workqueue; + +#define tegra_cpu_is_throttling() (is_throttling) + +static bool tegra_throttling_needed(unsigned long *rate) +{ + unsigned int current_freq = tegra_getspeed(0); + int i; + + for (i = THROTTLE_END_INDEX; i >= THROTTLE_START_INDEX; i--) { + if (freq_table[i].frequency < current_freq) { + *rate = freq_table[i].frequency; + return true; + } + } + + return false; +} + +static void tegra_throttle_work_func(struct work_struct *work) +{ + unsigned long rate; + + mutex_lock(&tegra_cpu_lock); + + if (tegra_throttling_needed(&rate) && tegra_update_cpu_speed(rate) == 0) { + queue_delayed_work(workqueue, &throttle_work, THROTTLE_DELAY); + } + + mutex_unlock(&tegra_cpu_lock); +} + +/** + * tegra_throttling_enable + * This function may sleep + */ +void tegra_throttling_enable(void) +{ + mutex_lock(&throttling_lock); + + if (!is_throttling) { + is_throttling = true; + queue_delayed_work(workqueue, &throttle_work, NO_DELAY); + } + + mutex_unlock(&throttling_lock); +} +EXPORT_SYMBOL_GPL(tegra_throttling_enable); + +/** + * tegra_throttling_disable + * This function may sleep + */ +void tegra_throttling_disable(void) +{ + mutex_lock(&throttling_lock); + + if (is_throttling) { + cancel_delayed_work_sync(&throttle_work); + is_throttling = false; + } + mutex_unlock(&throttling_lock); +} +EXPORT_SYMBOL_GPL(tegra_throttling_disable); + +#ifdef CONFIG_DEBUG_FS +static int throttle_debug_set(void *data, u64 val) +{ + if (val) { + tegra_throttling_enable(); + } else { + tegra_throttling_disable(); + } + + return 0; +} +static int throttle_debug_get(void *data, u64 *val) +{ + *val = (u64) is_throttling; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(throttle_fops, throttle_debug_get, throttle_debug_set, "%llu\n"); + +static struct dentry *cpu_tegra_debugfs_root; + +static int __init tegra_cpu_debug_init(void) +{ + cpu_tegra_debugfs_root = debugfs_create_dir("cpu-tegra", 0); + + if (!cpu_tegra_debugfs_root) + return -ENOMEM; + + if (!debugfs_create_file("throttle", 0644, cpu_tegra_debugfs_root, NULL, &throttle_fops)) + goto err_out; + + return 0; + +err_out: + debugfs_remove_recursive(cpu_tegra_debugfs_root); + return -ENOMEM; + +} + +static void __exit tegra_cpu_debug_exit(void) +{ + debugfs_remove_recursive(cpu_tegra_debugfs_root); +} + +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) { @@ -169,8 +288,7 @@ static int tegra_target(struct cpufreq_policy *policy, { int idx; unsigned int freq; - unsigned int highest_speed; - unsigned int limit_when_throttling; + unsigned int new_speed; int ret = 0; mutex_lock(&tegra_cpu_lock); @@ -187,136 +305,30 @@ static int tegra_target(struct cpufreq_policy *policy, target_cpu_speed[policy->cpu] = freq; - highest_speed = tegra_cpu_highest_speed(); + new_speed = tegra_cpu_highest_speed(); /* 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; + + 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(highest_speed); + ret = tegra_update_cpu_speed(new_speed); out: mutex_unlock(&tegra_cpu_lock); return ret; } -static bool tegra_throttling_needed(unsigned long *rate) -{ - unsigned int current_freq = tegra_getspeed(0); - int i; - - for (i = THROTTLE_END_INDEX; i >= THROTTLE_START_INDEX; i--) { - if (freq_table[i].frequency < current_freq) { - *rate = freq_table[i].frequency; - return true; - } - } - - return false; -} - -static void tegra_throttle_work_func(struct work_struct *work) -{ - unsigned long rate; - - mutex_lock(&tegra_cpu_lock); - - if (tegra_throttling_needed(&rate) && tegra_update_cpu_speed(rate) == 0) { - queue_delayed_work(workqueue, &throttle_work, THROTTLE_DELAY); - } - - mutex_unlock(&tegra_cpu_lock); -} - -/** - * tegra_throttling_enable - * This functions may sleep - */ -void tegra_throttling_enable(void) -{ - mutex_lock(&throttling_lock); - - if (!is_throttling) { - is_throttling = true; - queue_delayed_work(workqueue, &throttle_work, NO_DELAY); - } - - mutex_unlock(&throttling_lock); -} -EXPORT_SYMBOL_GPL(tegra_throttling_enable); - -/** - * tegra_throttling_disable - * This functions may sleep - */ -void tegra_throttling_disable(void) -{ - mutex_lock(&throttling_lock); - - if (is_throttling) { - cancel_delayed_work_sync(&throttle_work); - is_throttling = false; - } - - mutex_unlock(&throttling_lock); -} -EXPORT_SYMBOL_GPL(tegra_throttling_disable); - -#ifdef CONFIG_DEBUG_FS -static int throttle_debug_set(void *data, u64 val) -{ - if (val) { - tegra_throttling_enable(); - } else { - tegra_throttling_disable(); - } - - return 0; -} -static int throttle_debug_get(void *data, u64 *val) -{ - *val = (u64) is_throttling; - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(throttle_fops, throttle_debug_get, throttle_debug_set, "%llu\n"); - -static struct dentry *cpu_tegra_debugfs_root; - -static int __init tegra_cpu_debug_init(void) -{ - cpu_tegra_debugfs_root = debugfs_create_dir("cpu-tegra", 0); - - if (!cpu_tegra_debugfs_root) - return -ENOMEM; - - if (!debugfs_create_file("throttle", 0644, cpu_tegra_debugfs_root, NULL, &throttle_fops)) - goto err_out; - - return 0; - -err_out: - debugfs_remove_recursive(cpu_tegra_debugfs_root); - return -ENOMEM; - -} - -static void __exit tegra_cpu_debug_exit(void) -{ - debugfs_remove_recursive(cpu_tegra_debugfs_root); -} - -late_initcall(tegra_cpu_debug_init); -module_exit(tegra_cpu_debug_exit); -#endif static int tegra_pm_notify(struct notifier_block *nb, unsigned long event, void *dummy) @@ -367,7 +379,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); } @@ -400,15 +411,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); }