c1f872c0e61c5186752f896a3afd7bcbd78eb13a
[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         int i;
155
156         while (tegra2_cpu_is_resettable_soon())
157                 cpu_relax();
158
159         if (tegra2_reset_other_cpus(dev->cpu))
160                 return -EBUSY;
161
162         if (request < state->target_residency) {
163                 /* Not enough time left to enter LP2 */
164                 tegra_cpu_wfi();
165                 return -EBUSY;
166         }
167
168         tegra_idle_lp2_last(request, 0);
169
170         for_each_online_cpu(i) {
171                 if (i != dev->cpu) {
172                         tegra2_wake_reset_cpu(i);
173                         tegra_clear_cpu_in_lp2(i);
174                 }
175         }
176
177         return 0;
178 }
179
180 void tegra2_idle_lp2(struct cpuidle_device *dev,
181                         struct cpuidle_state *state)
182 {
183         s64 request = ktime_to_us(tick_nohz_get_sleep_length());
184         bool last_cpu = tegra_set_cpu_in_lp2(dev->cpu);
185
186         cpu_pm_enter();
187
188         if (last_cpu) {
189                 if (tegra2_idle_lp2_last(dev, state, request) < 0) {
190                         int i;
191                         for_each_online_cpu(i) {
192                                 if (i != dev->cpu) {
193                                         tegra2_wake_reset_cpu(i);
194                                         tegra_clear_cpu_in_lp2(i);
195                                 }
196                         }
197                 }
198         } else
199                 tegra2_sleep_wfi(PHYS_OFFSET - PAGE_OFFSET);
200
201         cpu_pm_exit();
202         tegra_clear_cpu_in_lp2(dev->cpu);
203 }
204
205 void tegra2_cpu_idle_stats_lp2_ready(unsigned int cpu)
206 {
207         idle_stats.cpu_ready_count[cpu]++;
208 }
209
210 void tegra2_cpu_idle_stats_lp2_time(unsigned int cpu, s64 us)
211 {
212         idle_stats.cpu_wants_lp2_time[cpu] += us;
213 }
214
215 #ifdef CONFIG_DEBUG_FS
216 int tegra2_lp2_debug_show(struct seq_file *s, void *data)
217 {
218         int bin;
219         int i;
220         seq_printf(s, "                                    cpu0     cpu1\n");
221         seq_printf(s, "-------------------------------------------------\n");
222         seq_printf(s, "cpu ready:                      %8u %8u\n",
223                 idle_stats.cpu_ready_count[0],
224                 idle_stats.cpu_ready_count[1]);
225         seq_printf(s, "both idle:      %8u        %7u%% %7u%%\n",
226                 idle_stats.both_idle_count,
227                 idle_stats.both_idle_count * 100 /
228                         (idle_stats.cpu_ready_count[0] ?: 1),
229                 idle_stats.both_idle_count * 100 /
230                         (idle_stats.cpu_ready_count[1] ?: 1));
231         seq_printf(s, "tear down:      %8u %7u%%\n", idle_stats.tear_down_count,
232                 idle_stats.tear_down_count * 100 /
233                         (idle_stats.both_idle_count ?: 1));
234         seq_printf(s, "lp2:            %8u %7u%%\n", idle_stats.lp2_count,
235                 idle_stats.lp2_count * 100 /
236                         (idle_stats.both_idle_count ?: 1));
237
238         seq_printf(s, "\n");
239         seq_printf(s, "cpu ready time:                 %8llu %8llu ms\n",
240                 div64_u64(idle_stats.cpu_wants_lp2_time[0], 1000),
241                 div64_u64(idle_stats.cpu_wants_lp2_time[1], 1000));
242         seq_printf(s, "lp2 time:       %8llu ms     %7d%% %7d%%\n",
243                 div64_u64(idle_stats.in_lp2_time, 1000),
244                 (int)div64_u64(idle_stats.in_lp2_time * 100,
245                         idle_stats.cpu_wants_lp2_time[0] ?: 1),
246                 (int)div64_u64(idle_stats.in_lp2_time * 100,
247                         idle_stats.cpu_wants_lp2_time[1] ?: 1));
248
249         seq_printf(s, "\n");
250         seq_printf(s, "%19s %8s\n", "", "lp2");
251         seq_printf(s, "-------------------------------------------------\n");
252         for (bin = 0; bin < 32; bin++) {
253                 if (idle_stats.lp2_count_bin[bin] == 0)
254                         continue;
255                 seq_printf(s, "%6u - %6u ms: %8u\n",
256                         1 << (bin - 1), 1 << bin,
257                         idle_stats.lp2_count_bin[bin]);
258         }
259
260         seq_printf(s, "\n");
261         seq_printf(s, "%3s %20s %6s %10s\n",
262                 "int", "name", "count", "last count");
263         seq_printf(s, "--------------------------------------------\n");
264         for (i = 0; i < NR_IRQS; i++) {
265                 if (idle_stats.lp2_int_count[i] == 0)
266                         continue;
267                 seq_printf(s, "%3d %20s %6d %10d\n",
268                         i, irq_to_desc(i)->action ?
269                                 irq_to_desc(i)->action->name ?: "???" : "???",
270                         idle_stats.lp2_int_count[i],
271                         idle_stats.lp2_int_count[i] -
272                                 idle_stats.last_lp2_int_count[i]);
273                 idle_stats.last_lp2_int_count[i] = idle_stats.lp2_int_count[i];
274         };
275         return 0;
276 }
277 #endif