summaryrefslogtreecommitdiff
path: root/arch/arm
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2011-08-10 14:42:54 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:48:16 -0800
commit5ff305a4b37bd8cf481897f5a120354c845c91f7 (patch)
treebe4854d232e1b26dec2b996edf45cbcbc14f2301 /arch/arm
parent1a79f26ba8f78abf4d697efc118ef83c127b6c90 (diff)
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 <afrid@nvidia.com> Reviewed-by: Bitan Biswas <bbiswas@nvidia.com> Reviewed-by: Scott Williams <scwilliams@nvidia.com> Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com> Reviewed-by: Anshul Jain (SW) <anshulj@nvidia.com> Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com> Rebase-Id: R2340f78e1d22942022e171044d6b20f260e2d312
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-tegra/Makefile3
-rw-r--r--arch/arm/mach-tegra/cpu-tegra.c122
-rw-r--r--arch/arm/mach-tegra/cpu-tegra.h18
-rw-r--r--arch/arm/mach-tegra/tegra2-throttle.c180
4 files changed, 208 insertions, 115 deletions
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 <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 */
+