summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/tegra2_throttle.c
diff options
context:
space:
mode:
authorTom Cherry <tcherry@nvidia.com>2011-09-30 16:11:48 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:49:47 -0800
commit835b8822342551740fd3a7caaa0d0b4db7cd008c (patch)
tree4f67fab904f7b33324f4ee645fc93756e837e770 /arch/arm/mach-tegra/tegra2_throttle.c
parent8dca2269d34f694a9a02934ad750f42d8cadcf97 (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.c180
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 */
+