aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-tegra/cpuidle-t2.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/cpuidle-t2.c')
-rw-r--r--arch/arm/mach-tegra/cpuidle-t2.c413
1 files changed, 413 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/cpuidle-t2.c b/arch/arm/mach-tegra/cpuidle-t2.c
new file mode 100644
index 00000000000..e5ff7c61f24
--- /dev/null
+++ b/arch/arm/mach-tegra/cpuidle-t2.c
@@ -0,0 +1,413 @@
1/*
2 * arch/arm/mach-tegra/cpuidle-t2.c
3 *
4 * CPU idle driver for Tegra2 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 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 */
25
26#include <linux/kernel.h>
27#include <linux/cpu.h>
28#include <linux/cpuidle.h>
29#include <linux/debugfs.h>
30#include <linux/delay.h>
31#include <linux/init.h>
32#include <linux/interrupt.h>
33#include <linux/irq.h>
34#include <linux/io.h>
35#include <linux/sched.h>
36#include <linux/seq_file.h>
37#include <linux/slab.h>
38#include <linux/smp.h>
39#include <linux/suspend.h>
40#include <linux/tick.h>
41
42#include <asm/cpu_pm.h>
43
44#include <mach/iomap.h>
45#include <mach/irqs.h>
46
47#include "cpuidle.h"
48#include "gic.h"
49#include "pm.h"
50#include "sleep.h"
51#include "timer.h"
52
53static struct {
54 unsigned int cpu_ready_count[2];
55 unsigned long long cpu_wants_lp2_time[2];
56 unsigned long long in_lp2_time;
57 unsigned int both_idle_count;
58 unsigned int tear_down_count;
59 unsigned int lp2_count;
60 unsigned int lp2_completed_count;
61 unsigned int lp2_count_bin[32];
62 unsigned int lp2_completed_count_bin[32];
63 unsigned int lp2_int_count[NR_IRQS];
64 unsigned int last_lp2_int_count[NR_IRQS];
65} idle_stats;
66
67static inline unsigned int time_to_bin(unsigned int time)
68{
69 return fls(time);
70}
71
72#ifdef CONFIG_SMP
73
74#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX 0x4C
75#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR 0x344
76
77static void __iomem *clk_rst = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
78static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
79static s64 tegra_cpu1_wake_by_time = LLONG_MAX;
80
81static int tegra2_reset_sleeping_cpu(int cpu)
82{
83 int ret = 0;
84
85 BUG_ON(cpu == 0);
86 BUG_ON(cpu == smp_processor_id());
87 tegra_pen_lock();
88
89 if (readl(pmc + PMC_SCRATCH41) == CPU_RESETTABLE)
90 tegra2_cpu_reset(cpu);
91 else
92 ret = -EINVAL;
93
94 tegra_pen_unlock();
95
96 return ret;
97}
98
99static void tegra2_wake_reset_cpu(int cpu)
100{
101 u32 reg;
102
103 BUG_ON(cpu == 0);
104 BUG_ON(cpu == smp_processor_id());
105
106 tegra_pen_lock();
107
108 tegra2_cpu_clear_resettable();
109
110 /* enable cpu clock on cpu */
111 reg = readl(clk_rst + 0x4c);
112 writel(reg & ~(1 << (8 + cpu)),
113 clk_rst + CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
114
115 /* take the CPU out of reset */
116 reg = 0x1111 << cpu;
117 writel(reg, clk_rst +
118 CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
119
120 /* unhalt the cpu */
121 flowctrl_writel(0, FLOW_CTRL_HALT_CPU(1));
122
123 tegra_pen_unlock();
124}
125
126static int tegra2_reset_other_cpus(int cpu)
127{
128 int i;
129 int ret = 0;
130
131 BUG_ON(cpu != 0);
132
133 for_each_online_cpu(i) {
134 if (i != cpu) {
135 if (tegra2_reset_sleeping_cpu(i)) {
136 ret = -EBUSY;
137 break;
138 }
139 }
140 }
141
142 if (ret) {
143 for_each_online_cpu(i) {
144 if (i != cpu)
145 tegra2_wake_reset_cpu(i);
146 }
147 return ret;
148 }
149
150 return 0;
151}
152#else
153static void tegra2_wake_reset_cpu(int cpu)
154{
155}
156
157static int tegra2_reset_other_cpus(int cpu)
158{
159 return 0;
160}
161#endif
162
163bool tegra2_lp2_is_allowed(struct cpuidle_device *dev,
164 struct cpuidle_state *state)
165{
166 s64 request = ktime_to_us(tick_nohz_get_sleep_length());
167
168 if (request < state->target_residency) {
169 /* Not enough time left to enter LP2 */
170 return false;
171 }
172
173 return true;
174}
175
176static inline void tegra2_lp3_fall_back(struct cpuidle_device *dev)
177{
178 /* Not enough time left to enter LP2 */
179 tegra_cpu_wfi();
180
181 /* fall back here from LP2 path - tell cpuidle governor */
182 dev->last_state = &dev->states[0];
183}
184
185static int tegra2_idle_lp2_cpu_0(struct cpuidle_device *dev,
186 struct cpuidle_state *state, s64 request)
187{
188 ktime_t entry_time;
189 ktime_t exit_time;
190 s64 wake_time;
191 bool sleep_completed = false;
192 int bin;
193 int i;
194
195 while (tegra2_cpu_is_resettable_soon())
196 cpu_relax();
197
198 if (tegra2_reset_other_cpus(dev->cpu))
199 return 0;
200
201 idle_stats.both_idle_count++;
202
203 if (request < state->target_residency) {
204 tegra2_lp3_fall_back(dev);
205 return -EBUSY;
206 }
207
208 /* LP2 entry time */
209 entry_time = ktime_get();
210
211 /* LP2 initial targeted wake time */
212 wake_time = ktime_to_us(entry_time) + request;
213
214 /* CPU0 must wake up before CPU1. */
215 smp_rmb();
216 wake_time = min_t(s64, wake_time, tegra_cpu1_wake_by_time);
217
218 /* LP2 actual targeted wake time */
219 request = wake_time - ktime_to_us(entry_time);
220 BUG_ON(wake_time < 0LL);
221
222 idle_stats.tear_down_count++;
223 entry_time = ktime_get();
224
225 if (request > state->target_residency) {
226 s64 sleep_time = request - tegra_lp2_exit_latency;
227
228 bin = time_to_bin((u32)request / 1000);
229 idle_stats.lp2_count++;
230 idle_stats.lp2_count_bin[bin]++;
231
232 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
233
234 if (tegra_idle_lp2_last(sleep_time, 0) == 0)
235 sleep_completed = true;
236 else {
237 int irq = tegra_gic_pending_interrupt();
238 idle_stats.lp2_int_count[irq]++;
239 }
240
241 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
242 }
243
244 for_each_online_cpu(i) {
245 if (i != dev->cpu)
246 tegra2_wake_reset_cpu(i);
247 }
248
249 exit_time = ktime_get();
250 if (sleep_completed) {
251 /*
252 * Stayed in LP2 for the full time until the next tick,
253 * adjust the exit latency based on measurement
254 */
255 s64 actual_time = ktime_to_us(ktime_sub(exit_time, entry_time));
256 long offset = (long)(actual_time - request);
257 int latency = tegra_lp2_exit_latency + offset / 16;
258 latency = clamp(latency, 0, 10000);
259 tegra_lp2_exit_latency = latency;
260 smp_wmb();
261
262 idle_stats.lp2_completed_count++;
263 idle_stats.lp2_completed_count_bin[bin]++;
264 idle_stats.in_lp2_time += actual_time;
265
266 pr_debug("%lld %lld %ld %d\n", request, actual_time,
267 offset, bin);
268 }
269
270 return 0;
271}
272
273static void tegra2_idle_lp2_cpu_1(struct cpuidle_device *dev,
274 struct cpuidle_state *state, s64 request)
275{
276#ifdef CONFIG_SMP
277 struct tegra_twd_context twd_context;
278
279 if (request < tegra_lp2_exit_latency) {
280 tegra2_cpu_clear_resettable();
281 tegra2_lp3_fall_back(dev);
282 return;
283 }
284
285 /* Save time this CPU must be awakened by. */
286 tegra_cpu1_wake_by_time = ktime_to_us(ktime_get()) + request;
287 smp_wmb();
288
289 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
290
291 tegra_twd_suspend(&twd_context);
292
293 tegra2_sleep_wfi(PLAT_PHYS_OFFSET - PAGE_OFFSET);
294
295 tegra2_cpu_clear_resettable();
296
297 tegra_cpu1_wake_by_time = LLONG_MAX;
298
299 tegra_twd_resume(&twd_context);
300
301 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
302#endif
303}
304
305void tegra2_idle_lp2(struct cpuidle_device *dev,
306 struct cpuidle_state *state)
307{
308 s64 request = ktime_to_us(tick_nohz_get_sleep_length());
309 bool last_cpu = tegra_set_cpu_in_lp2(dev->cpu);
310
311 cpu_pm_enter();
312
313 if (dev->cpu == 0) {
314 if (last_cpu) {
315 if (tegra2_idle_lp2_cpu_0(dev, state, request) < 0) {
316 int i;
317 for_each_online_cpu(i) {
318 if (i != dev->cpu)
319 tegra2_wake_reset_cpu(i);
320 }
321 }
322 } else {
323 tegra2_lp3_fall_back(dev);
324 }
325 } else {
326 BUG_ON(last_cpu);
327 tegra2_idle_lp2_cpu_1(dev, state, request);
328 }
329
330 cpu_pm_exit();
331 tegra_clear_cpu_in_lp2(dev->cpu);
332}
333
334void tegra2_cpu_idle_stats_lp2_ready(unsigned int cpu)
335{
336 idle_stats.cpu_ready_count[cpu]++;
337}
338
339void tegra2_cpu_idle_stats_lp2_time(unsigned int cpu, s64 us)
340{
341 idle_stats.cpu_wants_lp2_time[cpu] += us;
342}
343
344#ifdef CONFIG_DEBUG_FS
345int tegra2_lp2_debug_show(struct seq_file *s, void *data)
346{
347 int bin;
348 int i;
349 seq_printf(s, " cpu0 cpu1\n");
350 seq_printf(s, "-------------------------------------------------\n");
351 seq_printf(s, "cpu ready: %8u %8u\n",
352 idle_stats.cpu_ready_count[0],
353 idle_stats.cpu_ready_count[1]);
354 seq_printf(s, "both idle: %8u %7u%% %7u%%\n",
355 idle_stats.both_idle_count,
356 idle_stats.both_idle_count * 100 /
357 (idle_stats.cpu_ready_count[0] ?: 1),
358 idle_stats.both_idle_count * 100 /
359 (idle_stats.cpu_ready_count[1] ?: 1));
360 seq_printf(s, "tear down: %8u %7u%%\n", idle_stats.tear_down_count,
361 idle_stats.tear_down_count * 100 /
362 (idle_stats.both_idle_count ?: 1));
363 seq_printf(s, "lp2: %8u %7u%%\n", idle_stats.lp2_count,
364 idle_stats.lp2_count * 100 /
365 (idle_stats.both_idle_count ?: 1));
366 seq_printf(s, "lp2 completed: %8u %7u%%\n",
367 idle_stats.lp2_completed_count,
368 idle_stats.lp2_completed_count * 100 /
369 (idle_stats.lp2_count ?: 1));
370
371 seq_printf(s, "\n");
372 seq_printf(s, "cpu ready time: %8llu %8llu ms\n",
373 div64_u64(idle_stats.cpu_wants_lp2_time[0], 1000),
374 div64_u64(idle_stats.cpu_wants_lp2_time[1], 1000));
375 seq_printf(s, "lp2 time: %8llu ms %7d%% %7d%%\n",
376 div64_u64(idle_stats.in_lp2_time, 1000),
377 (int)div64_u64(idle_stats.in_lp2_time * 100,
378 idle_stats.cpu_wants_lp2_time[0] ?: 1),
379 (int)div64_u64(idle_stats.in_lp2_time * 100,
380 idle_stats.cpu_wants_lp2_time[1] ?: 1));
381
382 seq_printf(s, "\n");
383 seq_printf(s, "%19s %8s %8s %8s\n", "", "lp2", "comp", "%");
384 seq_printf(s, "-------------------------------------------------\n");
385 for (bin = 0; bin < 32; bin++) {
386 if (idle_stats.lp2_count_bin[bin] == 0)
387 continue;
388 seq_printf(s, "%6u - %6u ms: %8u %8u %7u%%\n",
389 1 << (bin - 1), 1 << bin,
390 idle_stats.lp2_count_bin[bin],
391 idle_stats.lp2_completed_count_bin[bin],
392 idle_stats.lp2_completed_count_bin[bin] * 100 /
393 idle_stats.lp2_count_bin[bin]);
394 }
395
396 seq_printf(s, "\n");
397 seq_printf(s, "%3s %20s %6s %10s\n",
398 "int", "name", "count", "last count");
399 seq_printf(s, "--------------------------------------------\n");
400 for (i = 0; i < NR_IRQS; i++) {
401 if (idle_stats.lp2_int_count[i] == 0)
402 continue;
403 seq_printf(s, "%3d %20s %6d %10d\n",
404 i, irq_to_desc(i)->action ?
405 irq_to_desc(i)->action->name ?: "???" : "???",
406 idle_stats.lp2_int_count[i],
407 idle_stats.lp2_int_count[i] -
408 idle_stats.last_lp2_int_count[i]);
409 idle_stats.last_lp2_int_count[i] = idle_stats.lp2_int_count[i];
410 };
411 return 0;
412}
413#endif