ARM: tegra: Catch early LP2 exits
[linux-3.10.git] / arch / arm / mach-tegra / cpuidle-t2.c
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 #include <linux/cpu_pm.h>
42
43 #include <mach/iomap.h>
44 #include <mach/irqs.h>
45
46 #include "cpuidle.h"
47 #include "flowctrl.h"
48 #include "pm.h"
49 #include "sleep.h"
50
51 static struct {
52         unsigned int cpu_ready_count[2];
53         unsigned long long cpu_wants_lp2_time[2];
54         unsigned long long in_lp2_time;
55         unsigned int both_idle_count;
56         unsigned int tear_down_count;
57         unsigned int lp2_count;
58         unsigned int lp2_count_bin[32];
59         unsigned int lp2_int_count[NR_IRQS];
60         unsigned int last_lp2_int_count[NR_IRQS];
61 } idle_stats;
62
63 #ifdef CONFIG_SMP
64
65 static void __iomem *clk_rst = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
66 static void __iomem *evp_reset = IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100;
67 static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
68
69 static int tegra2_reset_sleeping_cpu(int cpu)
70 {
71         int ret = 0;
72
73         BUG_ON(cpu == smp_processor_id());
74         tegra_pen_lock();
75
76         if (readl(pmc + PMC_SCRATCH41) == CPU_RESETTABLE)
77                 tegra2_cpu_reset(cpu);
78         else
79                 ret = -EINVAL;
80
81         tegra_pen_unlock();
82
83         return ret;
84 }
85
86 static void tegra2_wake_reset_cpu(int cpu)
87 {
88         u32 reg;
89
90         writel(virt_to_phys(tegra_secondary_resume), evp_reset);
91
92         /* enable cpu clock on cpu */
93         reg = readl(clk_rst + 0x4c);
94         writel(reg & ~(1 << (8 + cpu)), clk_rst + 0x4c);
95
96         reg = 0x1111 << cpu;
97         writel(reg, clk_rst + 0x344);
98
99         /* unhalt the cpu */
100         flowctrl_write_cpu_halt(1, 0);
101 }
102
103 static int tegra2_reset_other_cpus(int cpu)
104 {
105         int i;
106         int abort = -1;
107
108         for_each_online_cpu(i) {
109                 if (i != cpu) {
110                         if (tegra2_reset_sleeping_cpu(i)) {
111                                 abort = i;
112                                 break;
113                         }
114                 }
115         }
116
117         if (abort >= 0) {
118                 for_each_online_cpu(i) {
119                         if (i != cpu && i < abort)
120                                 tegra2_wake_reset_cpu(i);
121                 }
122                 return -EINVAL;
123         }
124
125         return 0;
126 }
127 #else
128 static void tegra2_wake_reset_cpu(int cpu)
129 {
130 }
131
132 static int tegra2_reset_other_cpus(int cpu)
133 {
134         return 0;
135 }
136 #endif
137
138 bool tegra2_lp2_is_allowed(struct cpuidle_device *dev,
139                         struct cpuidle_state *state)
140 {
141         s64 request = ktime_to_us(tick_nohz_get_sleep_length());
142
143         if (request < state->target_residency) {
144                 /* Not enough time left to enter LP2 */
145                 return false;
146         }
147
148         return true;
149 }
150
151 static int tegra2_idle_lp2_last(struct cpuidle_device *dev,
152                         struct cpuidle_state *state, s64 request)
153 {
154         bool sleep_completed = false;
155         int i;
156
157         while (tegra2_cpu_is_resettable_soon())
158                 cpu_relax();
159
160         if (tegra2_reset_other_cpus(dev->cpu))
161                 return -EBUSY;
162
163         if (request < state->target_residency) {
164                 /* Not enough time left to enter LP2 */
165                 tegra_cpu_wfi();
166                 return -EBUSY;
167         }
168
169         if (tegra_idle_lp2_last(request, 0) == 0)
170                 sleep_completed = true;
171         else
172                 idle_stats.lp2_int_count[tegra_pending_interrupt()]++;
173
174         for_each_online_cpu(i) {
175                 if (i != dev->cpu) {
176                         tegra2_wake_reset_cpu(i);
177                         tegra_clear_cpu_in_lp2(i);
178                 }
179         }
180
181         if (sleep_completed) {
182                 /*
183                  * Stayed in LP2 for the full time until the next tick
184                  */
185                 pr_debug("%lld\n", request);
186         }
187
188         return 0;
189 }
190
191 void tegra2_idle_lp2(struct cpuidle_device *dev,
192                         struct cpuidle_state *state)
193 {
194         s64 request = ktime_to_us(tick_nohz_get_sleep_length());
195         bool last_cpu = tegra_set_cpu_in_lp2(dev->cpu);
196
197         cpu_pm_enter();
198
199         if (last_cpu) {
200                 if (tegra2_idle_lp2_last(dev, state, request) < 0) {
201                         int i;
202                         for_each_online_cpu(i) {
203                                 if (i != dev->cpu) {
204                                         tegra2_wake_reset_cpu(i);
205                                         tegra_clear_cpu_in_lp2(i);
206                                 }
207                         }
208                 }
209         } else
210                 tegra2_sleep_wfi(PHYS_OFFSET - PAGE_OFFSET);
211
212         cpu_pm_exit();
213         tegra_clear_cpu_in_lp2(dev->cpu);
214 }
215
216 void tegra2_cpu_idle_stats_lp2_ready(unsigned int cpu)
217 {
218         idle_stats.cpu_ready_count[cpu]++;
219 }
220
221 void tegra2_cpu_idle_stats_lp2_time(unsigned int cpu, s64 us)
222 {
223         idle_stats.cpu_wants_lp2_time[cpu] += us;
224 }
225
226 #ifdef CONFIG_DEBUG_FS
227 int tegra2_lp2_debug_show(struct seq_file *s, void *data)
228 {
229         int bin;
230         int i;
231         seq_printf(s, "                                    cpu0     cpu1\n");
232         seq_printf(s, "-------------------------------------------------\n");
233         seq_printf(s, "cpu ready:                      %8u %8u\n",
234                 idle_stats.cpu_ready_count[0],
235                 idle_stats.cpu_ready_count[1]);
236         seq_printf(s, "both idle:      %8u        %7u%% %7u%%\n",
237                 idle_stats.both_idle_count,
238                 idle_stats.both_idle_count * 100 /
239                         (idle_stats.cpu_ready_count[0] ?: 1),
240                 idle_stats.both_idle_count * 100 /
241                         (idle_stats.cpu_ready_count[1] ?: 1));
242         seq_printf(s, "tear down:      %8u %7u%%\n", idle_stats.tear_down_count,
243                 idle_stats.tear_down_count * 100 /
244                         (idle_stats.both_idle_count ?: 1));
245         seq_printf(s, "lp2:            %8u %7u%%\n", idle_stats.lp2_count,
246                 idle_stats.lp2_count * 100 /
247                         (idle_stats.both_idle_count ?: 1));
248
249         seq_printf(s, "\n");
250         seq_printf(s, "cpu ready time:                 %8llu %8llu ms\n",
251                 div64_u64(idle_stats.cpu_wants_lp2_time[0], 1000),
252                 div64_u64(idle_stats.cpu_wants_lp2_time[1], 1000));
253         seq_printf(s, "lp2 time:       %8llu ms     %7d%% %7d%%\n",
254                 div64_u64(idle_stats.in_lp2_time, 1000),
255                 (int)div64_u64(idle_stats.in_lp2_time * 100,
256                         idle_stats.cpu_wants_lp2_time[0] ?: 1),
257                 (int)div64_u64(idle_stats.in_lp2_time * 100,
258                         idle_stats.cpu_wants_lp2_time[1] ?: 1));
259
260         seq_printf(s, "\n");
261         seq_printf(s, "%19s %8s\n", "", "lp2");
262         seq_printf(s, "-------------------------------------------------\n");
263         for (bin = 0; bin < 32; bin++) {
264                 if (idle_stats.lp2_count_bin[bin] == 0)
265                         continue;
266                 seq_printf(s, "%6u - %6u ms: %8u\n",
267                         1 << (bin - 1), 1 << bin,
268                         idle_stats.lp2_count_bin[bin]);
269         }
270
271         seq_printf(s, "\n");
272         seq_printf(s, "%3s %20s %6s %10s\n",
273                 "int", "name", "count", "last count");
274         seq_printf(s, "--------------------------------------------\n");
275         for (i = 0; i < NR_IRQS; i++) {
276                 if (idle_stats.lp2_int_count[i] == 0)
277                         continue;
278                 seq_printf(s, "%3d %20s %6d %10d\n",
279                         i, irq_to_desc(i)->action ?
280                                 irq_to_desc(i)->action->name ?: "???" : "???",
281                         idle_stats.lp2_int_count[i],
282                         idle_stats.lp2_int_count[i] -
283                                 idle_stats.last_lp2_int_count[i]);
284                 idle_stats.last_lp2_int_count[i] = idle_stats.lp2_int_count[i];
285         };
286         return 0;
287 }
288 #endif