Ventana: KBC: Removing the KBC usage on ventana
[linux-2.6.git] / arch / arm / mach-tegra / tegra2-throttle.c
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 */
39 static struct cpufreq_frequency_table *throttle_table;
40 static 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
45 static int is_throttling;
46 static int throttle_lowest_index;
47 static int throttle_highest_index;
48 static int throttle_index;
49 static int throttle_next_index;
50 static struct delayed_work throttle_work;
51 static struct workqueue_struct *workqueue;
52 static DEFINE_MUTEX(tegra_throttle_lock);
53
54 static 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         }
72 out:
73         mutex_unlock(cpu_throttle_lock);
74 }
75
76 /*
77  * tegra_throttling_enable
78  * This function may sleep
79  */
80 void 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 }
112 EXPORT_SYMBOL_GPL(tegra_throttling_enable);
113
114 unsigned 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
121 bool tegra_is_throttling(void)
122 {
123         return is_throttling;
124 }
125
126 int __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
152 void tegra_throttle_exit(void)
153 {
154         destroy_workqueue(workqueue);
155 }
156
157 #ifdef CONFIG_DEBUG_FS
158
159 static int throttle_debug_set(void *data, u64 val)
160 {
161         tegra_throttling_enable(val);
162         return 0;
163 }
164 static int throttle_debug_get(void *data, u64 *val)
165 {
166         *val = (u64) is_throttling;
167         return 0;
168 }
169 DEFINE_SIMPLE_ATTRIBUTE(throttle_fops, throttle_debug_get, throttle_debug_set,
170                         "%llu\n");
171
172 int __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