diff options
author | Tom Cherry <tcherry@nvidia.com> | 2011-09-30 16:11:48 -0700 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:49:47 -0800 |
commit | 835b8822342551740fd3a7caaa0d0b4db7cd008c (patch) | |
tree | 4f67fab904f7b33324f4ee645fc93756e837e770 /arch/arm/mach-tegra/tegra2_throttle.c | |
parent | 8dca2269d34f694a9a02934ad750f42d8cadcf97 (diff) |
arm: tegra: Rename tegra2/3-throttle to tegra2/3_throttle.
This is to keep consistency with tegra* files all of which use
underscores instead of dashes
Reviewed-on: http://git-master/r/55582
(cherry picked from commit 401f0018a27a18aafb9eac7d0bed6990c99c73cc)
Change-Id: I1a7066e6ac86f5876126ae54cee84f64fbc509f1
Reviewed-on: http://git-master/r/62251
Reviewed-by: Varun Colbert <vcolbert@nvidia.com>
Tested-by: Varun Colbert <vcolbert@nvidia.com>
Rebase-Id: Rd08530c8b72d63d3ed1c8557f1c47f481ed49044
Diffstat (limited to 'arch/arm/mach-tegra/tegra2_throttle.c')
-rw-r--r-- | arch/arm/mach-tegra/tegra2_throttle.c | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/tegra2_throttle.c b/arch/arm/mach-tegra/tegra2_throttle.c new file mode 100644 index 000000000000..6114b20c6f5c --- /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 <ccross@google.com> + * 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 <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/cpufreq.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/debugfs.h> + +#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 */ + |