aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-tegra/cpuidle.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/cpuidle.c')
-rw-r--r--arch/arm/mach-tegra/cpuidle.c322
1 files changed, 322 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c
new file mode 100644
index 00000000000..47d5996e596
--- /dev/null
+++ b/arch/arm/mach-tegra/cpuidle.c
@@ -0,0 +1,322 @@
1/*
2 * arch/arm/mach-tegra/cpuidle.c
3 *
4 * CPU idle driver for Tegra CPUs
5 *
6 * Copyright (c) 2010-2011, NVIDIA Corporation.
7 * Copyright (c) 2011 Google, Inc.
8 * Author: Colin Cross <ccross@android.com>
9 * Gary King <gking@nvidia.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful, but WITHOUT
17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 * more details.
20 */
21
22#include <linux/kernel.h>
23#include <linux/cpu.h>
24#include <linux/cpuidle.h>
25#include <linux/debugfs.h>
26#include <linux/delay.h>
27#include <linux/init.h>
28#include <linux/interrupt.h>
29#include <linux/irq.h>
30#include <linux/io.h>
31#include <linux/sched.h>
32#include <linux/seq_file.h>
33#include <linux/slab.h>
34#include <linux/smp.h>
35#include <linux/suspend.h>
36#include <linux/tick.h>
37
38#include <asm/cpu_pm.h>
39
40#include <mach/iomap.h>
41#include <mach/irqs.h>
42
43#include <trace/events/power.h>
44
45#include "cpuidle.h"
46#include "pm.h"
47#include "sleep.h"
48
49int tegra_lp2_exit_latency;
50static int tegra_lp2_power_off_time;
51static unsigned int tegra_lp2_min_residency;
52
53struct cpuidle_driver tegra_idle = {
54 .name = "tegra_idle",
55 .owner = THIS_MODULE,
56};
57
58static DEFINE_PER_CPU(struct cpuidle_device *, idle_devices);
59
60static int tegra_idle_enter_lp3(struct cpuidle_device *dev,
61 struct cpuidle_state *state)
62{
63 ktime_t enter, exit;
64 s64 us;
65
66 trace_power_start(POWER_CSTATE, 1, dev->cpu);
67
68 local_irq_disable();
69 local_fiq_disable();
70
71 enter = ktime_get();
72
73 tegra_cpu_wfi();
74
75 exit = ktime_sub(ktime_get(), enter);
76 us = ktime_to_us(exit);
77
78 local_fiq_enable();
79 local_irq_enable();
80 return (int)us;
81}
82
83static bool lp2_in_idle __read_mostly = false;
84
85#ifdef CONFIG_PM_SLEEP
86static bool lp2_in_idle_modifiable __read_mostly = true;
87static bool lp2_disabled_by_suspend;
88
89void tegra_lp2_in_idle(bool enable)
90{
91 /* If LP2 in idle is permanently disabled it can't be re-enabled. */
92 if (lp2_in_idle_modifiable) {
93 lp2_in_idle = enable;
94 lp2_in_idle_modifiable = enable;
95 if (!enable)
96 pr_warn("LP2 in idle disabled\n");
97 }
98}
99
100void tegra_lp2_update_target_residency(struct cpuidle_state *state)
101{
102 state->target_residency = state->exit_latency +
103 tegra_lp2_power_off_time;
104 if (state->target_residency < tegra_lp2_min_residency)
105 state->target_residency = tegra_lp2_min_residency;
106}
107
108static int tegra_idle_enter_lp2(struct cpuidle_device *dev,
109 struct cpuidle_state *state)
110{
111 ktime_t enter, exit;
112 s64 us;
113
114 if (!lp2_in_idle || lp2_disabled_by_suspend ||
115 !tegra_lp2_is_allowed(dev, state)) {
116 dev->last_state = &dev->states[0];
117 return tegra_idle_enter_lp3(dev, state);
118 }
119
120 local_irq_disable();
121 enter = ktime_get();
122
123 tegra_cpu_idle_stats_lp2_ready(dev->cpu);
124 tegra_idle_lp2(dev, state);
125
126 exit = ktime_sub(ktime_get(), enter);
127 us = ktime_to_us(exit);
128
129 local_irq_enable();
130
131 /* cpu clockevents may have been reset by powerdown */
132 hrtimer_peek_ahead_timers();
133
134 smp_rmb();
135
136 /* Update LP2 latency provided no fall back to LP3 */
137 if (state == dev->last_state) {
138 tegra_lp2_set_global_latency(state);
139 tegra_lp2_update_target_residency(state);
140 }
141 tegra_cpu_idle_stats_lp2_time(dev->cpu, us);
142
143 return (int)us;
144}
145#endif
146
147static int tegra_idle_prepare(struct cpuidle_device *dev)
148{
149#ifdef CONFIG_PM_SLEEP
150 if (lp2_in_idle)
151 dev->states[1].flags &= ~CPUIDLE_FLAG_IGNORE;
152 else
153 dev->states[1].flags |= CPUIDLE_FLAG_IGNORE;
154#endif
155
156 return 0;
157}
158
159static int tegra_cpuidle_register_device(unsigned int cpu)
160{
161 struct cpuidle_device *dev;
162 struct cpuidle_state *state;
163
164 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
165 if (!dev)
166 return -ENOMEM;
167
168 dev->state_count = 0;
169 dev->cpu = cpu;
170
171 state = &dev->states[0];
172 snprintf(state->name, CPUIDLE_NAME_LEN, "LP3");
173 snprintf(state->desc, CPUIDLE_DESC_LEN, "CPU flow-controlled");
174 state->exit_latency = 10;
175 state->target_residency = 10;
176 state->power_usage = 600;
177 state->flags = CPUIDLE_FLAG_TIME_VALID;
178 state->enter = tegra_idle_enter_lp3;
179 dev->safe_state = state;
180 dev->state_count++;
181
182#ifdef CONFIG_PM_SLEEP
183 state = &dev->states[1];
184 snprintf(state->name, CPUIDLE_NAME_LEN, "LP2");
185 snprintf(state->desc, CPUIDLE_DESC_LEN, "CPU power-gate");
186 state->exit_latency = tegra_cpu_power_good_time();
187
188 state->target_residency = tegra_cpu_power_off_time() +
189 tegra_cpu_power_good_time();
190 if (state->target_residency < tegra_lp2_min_residency)
191 state->target_residency = tegra_lp2_min_residency;
192 state->power_usage = 0;
193 state->flags = CPUIDLE_FLAG_TIME_VALID;
194 state->enter = tegra_idle_enter_lp2;
195
196 dev->power_specified = 1;
197 dev->safe_state = state;
198 dev->state_count++;
199#endif
200
201 dev->prepare = tegra_idle_prepare;
202
203 if (cpuidle_register_device(dev)) {
204 pr_err("CPU%u: failed to register idle device\n", cpu);
205 kfree(dev);
206 return -EIO;
207 }
208 per_cpu(idle_devices, cpu) = dev;
209 return 0;
210}
211
212static int tegra_cpuidle_pm_notify(struct notifier_block *nb,
213 unsigned long event, void *dummy)
214{
215#ifdef CONFIG_PM_SLEEP
216 if (event == PM_SUSPEND_PREPARE)
217 lp2_disabled_by_suspend = true;
218 else if (event == PM_POST_SUSPEND)
219 lp2_disabled_by_suspend = false;
220#endif
221
222 return NOTIFY_OK;
223}
224
225static struct notifier_block tegra_cpuidle_pm_notifier = {
226 .notifier_call = tegra_cpuidle_pm_notify,
227};
228
229static int __init tegra_cpuidle_init(void)
230{
231 unsigned int cpu;
232 int ret;
233
234 ret = cpuidle_register_driver(&tegra_idle);
235 if (ret)
236 return ret;
237
238#ifdef CONFIG_PM_SLEEP
239 tegra_lp2_min_residency = tegra_cpu_lp2_min_residency();
240 tegra_lp2_exit_latency = tegra_cpu_power_good_time();
241 tegra_lp2_power_off_time = tegra_cpu_power_off_time();
242
243 ret = tegra_cpudile_init_soc();
244 if (ret)
245 return ret;
246#endif
247
248 for_each_possible_cpu(cpu) {
249 if (tegra_cpuidle_register_device(cpu))
250 pr_err("CPU%u: error initializing idle loop\n", cpu);
251 }
252
253 register_pm_notifier(&tegra_cpuidle_pm_notifier);
254 return 0;
255}
256
257static void __exit tegra_cpuidle_exit(void)
258{
259 unregister_pm_notifier(&tegra_cpuidle_pm_notifier);
260 cpuidle_unregister_driver(&tegra_idle);
261}
262
263module_init(tegra_cpuidle_init);
264module_exit(tegra_cpuidle_exit);
265
266static int lp2_in_idle_set(const char *arg, const struct kernel_param *kp)
267{
268#ifdef CONFIG_PM_SLEEP
269 int ret;
270
271 /* If LP2 in idle is permanently disabled it can't be re-enabled. */
272 if (lp2_in_idle_modifiable) {
273 ret = param_set_bool(arg, kp);
274 return ret;
275 }
276#endif
277 return -ENODEV;
278}
279
280static int lp2_in_idle_get(char *buffer, const struct kernel_param *kp)
281{
282 return param_get_bool(buffer, kp);
283}
284
285static struct kernel_param_ops lp2_in_idle_ops = {
286 .set = lp2_in_idle_set,
287 .get = lp2_in_idle_get,
288};
289module_param_cb(lp2_in_idle, &lp2_in_idle_ops, &lp2_in_idle, 0644);
290
291#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_PM_SLEEP)
292static int tegra_lp2_debug_open(struct inode *inode, struct file *file)
293{
294 return single_open(file, tegra_lp2_debug_show, inode->i_private);
295}
296
297static const struct file_operations tegra_lp2_debug_ops = {
298 .open = tegra_lp2_debug_open,
299 .read = seq_read,
300 .llseek = seq_lseek,
301 .release = single_release,
302};
303
304static int __init tegra_cpuidle_debug_init(void)
305{
306 struct dentry *dir;
307 struct dentry *d;
308
309 dir = debugfs_create_dir("cpuidle", NULL);
310 if (!dir)
311 return -ENOMEM;
312
313 d = debugfs_create_file("lp2", S_IRUGO, dir, NULL,
314 &tegra_lp2_debug_ops);
315 if (!d)
316 return -ENOMEM;
317
318 return 0;
319}
320
321late_initcall(tegra_cpuidle_debug_init);
322#endif