aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-tegra/tegra2_throttle.c
diff options
context:
space:
mode:
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 00000000000..6114b20c6f5
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra2_throttle.c
@@ -0,0 +1,180 @@
1/*
2 * arch/arm/mach-tegra/tegra2_throttle.c
3 *
4 * Copyright (C) 2010 Google, Inc.
5 *
6 * Author:
7 * Colin Cross <ccross@google.com>
8 * Based on arch/arm/plat-omap/cpu-omap.c, (C) 2005 Nokia Corporation
9 *
10 * Copyright (C) 2010-2011 NVIDIA Corporation
11 *
12 * This software is licensed under the terms of the GNU General Public
13 * License version 2, as published by the Free Software Foundation, and
14 * may be copied, distributed, and modified under those terms.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 */
22
23#include <linux/kernel.h>
24#include <linux/module.h>
25#include <linux/types.h>
26#include <linux/sched.h>
27#include <linux/cpufreq.h>
28#include <linux/delay.h>
29#include <linux/init.h>
30#include <linux/err.h>
31#include <linux/clk.h>
32#include <linux/io.h>
33#include <linux/debugfs.h>
34
35#include "clock.h"
36#include "cpu-tegra.h"
37
38/* tegra throttling require frequencies in the table to be in ascending order */
39static struct cpufreq_frequency_table *throttle_table;
40static struct mutex *cpu_throttle_lock;
41
42/* CPU frequency is gradually lowered when throttling is enabled */
43#define THROTTLE_DELAY msecs_to_jiffies(2000)
44
45static int is_throttling;
46static int throttle_lowest_index;
47static int throttle_highest_index;
48static int throttle_index;
49static int throttle_next_index;
50static struct delayed_work throttle_work;
51static struct workqueue_struct *workqueue;
52static DEFINE_MUTEX(tegra_throttle_lock);
53
54static void tegra_throttle_work_func(struct work_struct *work)
55{
56 unsigned int current_freq;
57
58 mutex_lock(cpu_throttle_lock);
59 if (!is_throttling)
60 goto out;
61
62 current_freq = tegra_getspeed(0);
63 throttle_index = throttle_next_index;
64
65 if (throttle_table[throttle_index].frequency < current_freq)
66 tegra_cpu_set_speed_cap(NULL);
67
68 if (throttle_index > throttle_lowest_index) {
69 throttle_next_index = throttle_index - 1;
70 queue_delayed_work(workqueue, &throttle_work, THROTTLE_DELAY);
71 }
72out:
73 mutex_unlock(cpu_throttle_lock);
74}
75
76/*
77 * tegra_throttling_enable
78 * This function may sleep
79 */
80void tegra_throttling_enable(bool enable)
81{
82 mutex_lock(&tegra_throttle_lock);
83 mutex_lock(cpu_throttle_lock);
84
85 if (enable && !(is_throttling++)) {
86 unsigned int current_freq = tegra_getspeed(0);
87
88 for (throttle_index = throttle_highest_index;
89 throttle_index >= throttle_lowest_index;
90 throttle_index--)
91 if (throttle_table[throttle_index].frequency
92 < current_freq)
93 break;
94
95 throttle_index = max(throttle_index, throttle_lowest_index);
96 throttle_next_index = throttle_index;
97 queue_delayed_work(workqueue, &throttle_work, 0);
98 } else if (!enable && is_throttling) {
99 if (!(--is_throttling)) {
100 /* restore speed requested by governor */
101 tegra_cpu_set_speed_cap(NULL);
102
103 mutex_unlock(cpu_throttle_lock);
104 cancel_delayed_work_sync(&throttle_work);
105 mutex_unlock(&tegra_throttle_lock);
106 return;
107 }
108 }
109 mutex_unlock(cpu_throttle_lock);
110 mutex_unlock(&tegra_throttle_lock);
111}
112EXPORT_SYMBOL_GPL(tegra_throttling_enable);
113
114unsigned int tegra_throttle_governor_speed(unsigned int requested_speed)
115{
116 return is_throttling ?
117 min(requested_speed, throttle_table[throttle_index].frequency) :
118 requested_speed;
119}
120
121bool tegra_is_throttling(void)
122{
123 return is_throttling;
124}
125
126int __init tegra_throttle_init(struct mutex *cpu_lock)
127{
128 struct tegra_cpufreq_table_data *table_data =
129 tegra_cpufreq_table_get();
130 if (IS_ERR_OR_NULL(table_data))
131 return -EINVAL;
132
133 /*
134 * High-priority, others flags default: not bound to a specific
135 * CPU, has rescue worker task (in case of allocation deadlock,
136 * etc.). Single-threaded.
137 */
138 workqueue = alloc_workqueue("cpu-tegra",
139 WQ_HIGHPRI | WQ_UNBOUND | WQ_RESCUER, 1);
140 if (!workqueue)
141 return -ENOMEM;
142 INIT_DELAYED_WORK(&throttle_work, tegra_throttle_work_func);
143
144 throttle_lowest_index = table_data->throttle_lowest_index;
145 throttle_highest_index = table_data->throttle_highest_index;
146 throttle_table = table_data->freq_table;
147 cpu_throttle_lock = cpu_lock;
148
149 return 0;
150}
151
152void tegra_throttle_exit(void)
153{
154 destroy_workqueue(workqueue);
155}
156
157#ifdef CONFIG_DEBUG_FS
158
159static int throttle_debug_set(void *data, u64 val)
160{
161 tegra_throttling_enable(val);
162 return 0;
163}
164static int throttle_debug_get(void *data, u64 *val)
165{
166 *val = (u64) is_throttling;
167 return 0;
168}
169DEFINE_SIMPLE_ATTRIBUTE(throttle_fops, throttle_debug_get, throttle_debug_set,
170 "%llu\n");
171
172int __init tegra_throttle_debug_init(struct dentry *cpu_tegra_debugfs_root)
173{
174 if (!debugfs_create_file("throttle", 0644, cpu_tegra_debugfs_root,
175 NULL, &throttle_fops))
176 return -ENOMEM;
177 return 0;
178}
179#endif /* CONFIG_DEBUG_FS */
180