From 5ff305a4b37bd8cf481897f5a120354c845c91f7 Mon Sep 17 00:00:00 2001 From: Alex Frid Date: Wed, 10 Aug 2011 14:42:54 -0700 Subject: ARM: tegra: power: Separate throttling code Moved tegra CPU throttling algorithm implementation into a separate file. For now, the same algorithm is used for both Tegra2 and Tegra3 architecture. Original-Change-Id: I478c32b5adee4c946472129b89615580c10b41e1 Reviewed-on: http://git-master/r/46748 Tested-by: Aleksandr Frid Reviewed-by: Bitan Biswas Reviewed-by: Scott Williams Reviewed-by: Diwakar Tundlam Reviewed-by: Anshul Jain (SW) Reviewed-by: Yu-Huan Hsu Rebase-Id: R2340f78e1d22942022e171044d6b20f260e2d312 --- arch/arm/mach-tegra/Makefile | 3 + arch/arm/mach-tegra/cpu-tegra.c | 122 ++--------------------- arch/arm/mach-tegra/cpu-tegra.h | 18 ++++ arch/arm/mach-tegra/tegra2-throttle.c | 180 ++++++++++++++++++++++++++++++++++ 4 files changed, 208 insertions(+), 115 deletions(-) create mode 100644 arch/arm/mach-tegra/tegra2-throttle.c (limited to 'arch/arm') diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index bde11a82ce98..8776f13679f7 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -76,6 +76,9 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += cpuidle-t2.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += cpuidle-t3.o endif endif +ifeq ($(CONFIG_TEGRA_THERMAL_THROTTLE),y) +obj-y += tegra2-throttle.o +endif obj-$(CONFIG_TEGRA_IOVMM) += iovmm.o obj-$(CONFIG_TEGRA_IOVMM_GART) += iovmm-gart.o obj-$(CONFIG_TEGRA_IOVMM_SMMU) += iovmm-smmu.o diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c index aa2e5720b459..b0163f0311b5 100644 --- a/arch/arm/mach-tegra/cpu-tegra.c +++ b/arch/arm/mach-tegra/cpu-tegra.c @@ -54,108 +54,16 @@ static DEFINE_MUTEX(tegra_cpu_lock); static bool is_suspended; static int suspend_index; -unsigned int tegra_getspeed(unsigned int cpu); - #ifdef CONFIG_TEGRA_THERMAL_THROTTLE -/* CPU frequency is gradually lowered when throttling is enabled */ -#define THROTTLE_DELAY msecs_to_jiffies(2000) - -static int is_throttling; -static int throttle_lowest_index; -static int throttle_highest_index; -static int throttle_index; -static int throttle_next_index; -static struct delayed_work throttle_work; -static struct workqueue_struct *workqueue; -static DEFINE_MUTEX(tegra_throttle_lock); - -static void tegra_throttle_work_func(struct work_struct *work) -{ - unsigned int current_freq; - - mutex_lock(&tegra_cpu_lock); - if (!is_throttling) - goto out; - - current_freq = tegra_getspeed(0); - throttle_index = throttle_next_index; - - if (freq_table[throttle_index].frequency < current_freq) - tegra_cpu_set_speed_cap(NULL); - - if (throttle_index > throttle_lowest_index) { - throttle_next_index = throttle_index - 1; - queue_delayed_work(workqueue, &throttle_work, THROTTLE_DELAY); - } -out: - mutex_unlock(&tegra_cpu_lock); -} - -/* - * tegra_throttling_enable - * This function may sleep - */ -void tegra_throttling_enable(bool enable) -{ - mutex_lock(&tegra_throttle_lock); - mutex_lock(&tegra_cpu_lock); - - if (enable && !(is_throttling++)) { - unsigned int current_freq = tegra_getspeed(0); - - for (throttle_index = throttle_highest_index; - throttle_index >= throttle_lowest_index; - throttle_index--) - if (freq_table[throttle_index].frequency - < current_freq) - break; - - throttle_index = max(throttle_index, throttle_lowest_index); - throttle_next_index = throttle_index; - queue_delayed_work(workqueue, &throttle_work, 0); - } else if (!enable && is_throttling) { - if (!(--is_throttling)) { - /* restore speed requested by governor */ - tegra_cpu_set_speed_cap(NULL); - - mutex_unlock(&tegra_cpu_lock); - cancel_delayed_work_sync(&throttle_work); - mutex_unlock(&tegra_throttle_lock); - return; - } - } - mutex_unlock(&tegra_cpu_lock); - mutex_unlock(&tegra_throttle_lock); -} -EXPORT_SYMBOL_GPL(tegra_throttling_enable); - -static unsigned int throttle_governor_speed(unsigned int requested_speed) -{ - return is_throttling ? - min(requested_speed, freq_table[throttle_index].frequency) : - requested_speed; -} static ssize_t show_throttle(struct cpufreq_policy *policy, char *buf) { - return sprintf(buf, "%u\n", is_throttling); + return sprintf(buf, "%u\n", tegra_is_throttling()); } cpufreq_freq_attr_ro(throttle); #ifdef CONFIG_DEBUG_FS -static int throttle_debug_set(void *data, u64 val) -{ - tegra_throttling_enable(val); - 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; @@ -166,7 +74,7 @@ static int __init tegra_cpu_debug_init(void) if (!cpu_tegra_debugfs_root) return -ENOMEM; - if (!debugfs_create_file("throttle", 0644, cpu_tegra_debugfs_root, NULL, &throttle_fops)) + if (tegra_throttle_debug_init(cpu_tegra_debugfs_root)) goto err_out; return 0; @@ -185,9 +93,6 @@ 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 throttle_governor_speed(requested_speed) (requested_speed) #endif /* CONFIG_TEGRA_THERMAL_THROTTLE */ #ifdef CONFIG_TEGRA_EDP_LIMITS @@ -453,7 +358,7 @@ int tegra_cpu_set_speed_cap(unsigned int *speed_cap) if (is_suspended) return -EBUSY; - new_speed = throttle_governor_speed(new_speed); + new_speed = tegra_throttle_governor_speed(new_speed); new_speed = edp_governor_speed(new_speed); if (speed_cap) *speed_cap = new_speed; @@ -591,21 +496,10 @@ static int __init tegra_cpufreq_init(void) suspend_index = table_data->suspend_index; -#ifdef CONFIG_TEGRA_THERMAL_THROTTLE - /* - * High-priority, others flags default: not bound to a specific - * CPU, has rescue worker task (in case of allocation deadlock, - * etc.). Single-threaded. - */ - workqueue = alloc_workqueue("cpu-tegra", - WQ_HIGHPRI | WQ_UNBOUND | WQ_RESCUER, 1); - if (!workqueue) - return -ENOMEM; - INIT_DELAYED_WORK(&throttle_work, tegra_throttle_work_func); + ret = tegra_throttle_init(&tegra_cpu_lock); + if (ret) + return ret; - throttle_lowest_index = table_data->throttle_lowest_index; - throttle_highest_index = table_data->throttle_highest_index; -#endif ret = tegra_auto_hotplug_init(&tegra_cpu_lock); if (ret) return ret; @@ -617,9 +511,7 @@ static int __init tegra_cpufreq_init(void) static void __exit tegra_cpufreq_exit(void) { -#ifdef CONFIG_TEGRA_THERMAL_THROTTLE - destroy_workqueue(workqueue); -#endif + tegra_throttle_exit(); tegra_cpu_edp_exit(); tegra_auto_hotplug_exit(); cpufreq_unregister_driver(&tegra_cpufreq_driver); diff --git a/arch/arm/mach-tegra/cpu-tegra.h b/arch/arm/mach-tegra/cpu-tegra.h index eaa230574763..b47828f3c7b9 100644 --- a/arch/arm/mach-tegra/cpu-tegra.h +++ b/arch/arm/mach-tegra/cpu-tegra.h @@ -21,6 +21,7 @@ #ifndef __MACH_TEGRA_CPU_TEGRA_H #define __MACH_TEGRA_CPU_TEGRA_H +unsigned int tegra_getspeed(unsigned int cpu); int tegra_cpu_set_speed_cap(unsigned int *speed_cap); unsigned int tegra_count_slow_cpus(unsigned long speed_limit); unsigned int tegra_get_slowest_cpu_n(void); @@ -28,8 +29,25 @@ unsigned long tegra_cpu_lowest_speed(void); unsigned long tegra_cpu_highest_speed(void); #ifdef CONFIG_TEGRA_THERMAL_THROTTLE +int tegra_throttle_init(struct mutex *cpu_lock); +void tegra_throttle_exit(void); +bool tegra_is_throttling(void); +unsigned int tegra_throttle_governor_speed(unsigned int requested_speed); +int tegra_throttle_debug_init(struct dentry *cpu_tegra_debugfs_root); void tegra_throttling_enable(bool enable); #else +static inline int tegra_throttle_init(struct mutex *cpu_lock) +{ return 0; } +static inline void tegra_throttle_exit(void) +{} +static inline bool tegra_is_throttling(void) +{ return false; } +static inline unsigned int tegra_throttle_governor_speed( + unsigned int requested_speed) +{ return requested_speed; } +static inline int tegra_throttle_debug_init( + struct dentry *cpu_tegra_debugfs_root) +{ return 0; } #define tegra_throttling_enable NULL #endif /* CONFIG_TEGRA_THERMAL_THROTTLE */ diff --git a/arch/arm/mach-tegra/tegra2-throttle.c b/arch/arm/mach-tegra/tegra2-throttle.c new file mode 100644 index 000000000000..dd0d1d752242 --- /dev/null +++ b/arch/arm/mach-tegra/tegra2-throttle.c @@ -0,0 +1,180 @@ +/* + * arch/arm/mach-tegra/tegra2-throttle.c + * + * Copyright (C) 2010 Google, Inc. + * + * Author: + * Colin Cross + * Based on arch/arm/plat-omap/cpu-omap.c, (C) 2005 Nokia Corporation + * + * Copyright (C) 2010-2011 NVIDIA Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clock.h" +#include "cpu-tegra.h" + +/* tegra throttling require frequencies in the table to be in ascending order */ +static struct cpufreq_frequency_table *throttle_table; +static struct mutex *cpu_throttle_lock; + +/* CPU frequency is gradually lowered when throttling is enabled */ +#define THROTTLE_DELAY msecs_to_jiffies(2000) + +static int is_throttling; +static int throttle_lowest_index; +static int throttle_highest_index; +static int throttle_index; +static int throttle_next_index; +static struct delayed_work throttle_work; +static struct workqueue_struct *workqueue; +static DEFINE_MUTEX(tegra_throttle_lock); + +static void tegra_throttle_work_func(struct work_struct *work) +{ + unsigned int current_freq; + + mutex_lock(cpu_throttle_lock); + if (!is_throttling) + goto out; + + current_freq = tegra_getspeed(0); + throttle_index = throttle_next_index; + + if (throttle_table[throttle_index].frequency < current_freq) + tegra_cpu_set_speed_cap(NULL); + + if (throttle_index > throttle_lowest_index) { + throttle_next_index = throttle_index - 1; + queue_delayed_work(workqueue, &throttle_work, THROTTLE_DELAY); + } +out: + mutex_unlock(cpu_throttle_lock); +} + +/* + * tegra_throttling_enable + * This function may sleep + */ +void tegra_throttling_enable(bool enable) +{ + mutex_lock(&tegra_throttle_lock); + mutex_lock(cpu_throttle_lock); + + if (enable && !(is_throttling++)) { + unsigned int current_freq = tegra_getspeed(0); + + for (throttle_index = throttle_highest_index; + throttle_index >= throttle_lowest_index; + throttle_index--) + if (throttle_table[throttle_index].frequency + < current_freq) + break; + + throttle_index = max(throttle_index, throttle_lowest_index); + throttle_next_index = throttle_index; + queue_delayed_work(workqueue, &throttle_work, 0); + } else if (!enable && is_throttling) { + if (!(--is_throttling)) { + /* restore speed requested by governor */ + tegra_cpu_set_speed_cap(NULL); + + mutex_unlock(cpu_throttle_lock); + cancel_delayed_work_sync(&throttle_work); + mutex_unlock(&tegra_throttle_lock); + return; + } + } + mutex_unlock(cpu_throttle_lock); + mutex_unlock(&tegra_throttle_lock); +} +EXPORT_SYMBOL_GPL(tegra_throttling_enable); + +unsigned int tegra_throttle_governor_speed(unsigned int requested_speed) +{ + return is_throttling ? + min(requested_speed, throttle_table[throttle_index].frequency) : + requested_speed; +} + +bool tegra_is_throttling(void) +{ + return is_throttling; +} + +int __init tegra_throttle_init(struct mutex *cpu_lock) +{ + struct tegra_cpufreq_table_data *table_data = + tegra_cpufreq_table_get(); + if (IS_ERR_OR_NULL(table_data)) + return -EINVAL; + + /* + * High-priority, others flags default: not bound to a specific + * CPU, has rescue worker task (in case of allocation deadlock, + * etc.). Single-threaded. + */ + workqueue = alloc_workqueue("cpu-tegra", + WQ_HIGHPRI | WQ_UNBOUND | WQ_RESCUER, 1); + if (!workqueue) + return -ENOMEM; + INIT_DELAYED_WORK(&throttle_work, tegra_throttle_work_func); + + throttle_lowest_index = table_data->throttle_lowest_index; + throttle_highest_index = table_data->throttle_highest_index; + throttle_table = table_data->freq_table; + cpu_throttle_lock = cpu_lock; + + return 0; +} + +void tegra_throttle_exit(void) +{ + destroy_workqueue(workqueue); +} + +#ifdef CONFIG_DEBUG_FS + +static int throttle_debug_set(void *data, u64 val) +{ + tegra_throttling_enable(val); + 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"); + +int __init tegra_throttle_debug_init(struct dentry *cpu_tegra_debugfs_root) +{ + if (!debugfs_create_file("throttle", 0644, cpu_tegra_debugfs_root, + NULL, &throttle_fops)) + return -ENOMEM; + return 0; +} +#endif /* CONFIG_DEBUG_FS */ + -- cgit v1.2.3