Ventana: KBC: Removing the KBC usage on ventana
[linux-2.6.git] / arch / arm / mach-tegra / pm-irq.c
1 /*
2  * Copyright (C) 2011 Google, Inc.
3  *
4  * Author:
5  *      Colin Cross <ccross@android.com>
6  *
7  * This software is licensed under the terms of the GNU General Public
8  * License version 2, as published by the Free Software Foundation, and
9  * may be copied, distributed, and modified under those terms.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  */
17
18 #include <linux/kernel.h>
19 #include <linux/debugfs.h>
20 #include <linux/delay.h>
21 #include <linux/init.h>
22 #include <linux/interrupt.h>
23 #include <linux/irq.h>
24 #include <linux/io.h>
25 #include <linux/moduleparam.h>
26 #include <linux/seq_file.h>
27 #include <linux/syscore_ops.h>
28
29 #include <mach/iomap.h>
30
31 #include "pm-irq.h"
32
33 #define PMC_CTRL                0x0
34 #define PMC_CTRL_LATCH_WAKEUPS  (1 << 5)
35 #define PMC_WAKE_MASK           0xc
36 #define PMC_WAKE_LEVEL          0x10
37 #define PMC_WAKE_STATUS         0x14
38 #define PMC_SW_WAKE_STATUS      0x18
39 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
40 #define PMC_WAKE2_MASK          0x160
41 #define PMC_WAKE2_LEVEL         0x164
42 #define PMC_WAKE2_STATUS        0x168
43 #define PMC_SW_WAKE2_STATUS     0x16C
44 #endif
45
46 static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
47
48 static u64 tegra_lp0_wake_enb;
49 static u64 tegra_lp0_wake_level;
50 static u64 tegra_lp0_wake_level_any;
51 static int tegra_prevent_lp0;
52
53 static unsigned int tegra_wake_irq_count[64];
54
55 static bool debug_lp0;
56 module_param(debug_lp0, bool, S_IRUGO | S_IWUSR);
57
58 static bool warn_prevent_lp0;
59 module_param(warn_prevent_lp0, bool, S_IRUGO | S_IWUSR);
60
61 bool tegra_pm_irq_lp0_allowed(void)
62 {
63         return (tegra_prevent_lp0 == 0);
64 }
65
66 /* ensures that sufficient time is passed for a register write to
67  * serialize into the 32KHz domain */
68 static void pmc_32kwritel(u32 val, unsigned long offs)
69 {
70         writel(val, pmc + offs);
71         udelay(130);
72 }
73
74 static inline void write_pmc_wake_mask(u64 value)
75 {
76         writel((u32)value, pmc + PMC_WAKE_MASK);
77 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
78         __raw_writel((u32)(value >> 32), pmc + PMC_WAKE2_MASK);
79 #endif
80 }
81
82 static inline u64 read_pmc_wake_level(void)
83 {
84         u64 reg;
85
86 #ifdef CONFIG_ARCH_TEGRA_2x_SOC
87         reg = readl(pmc + PMC_WAKE_LEVEL);
88 #else
89         reg = __raw_readl(pmc + PMC_WAKE_LEVEL);
90         reg |= ((u64)readl(pmc + PMC_WAKE2_LEVEL)) << 32;
91 #endif
92         return reg;
93 }
94
95 static inline void write_pmc_wake_level(u64 value)
96 {
97         writel((u32)value, pmc + PMC_WAKE_LEVEL);
98 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
99         __raw_writel((u32)(value >> 32), pmc + PMC_WAKE2_LEVEL);
100 #endif
101 }
102
103 static inline u64 read_pmc_wake_status(void)
104 {
105         u64 reg;
106
107 #ifdef CONFIG_ARCH_TEGRA_2x_SOC
108         reg = readl(pmc + PMC_WAKE_STATUS);
109 #else
110         reg = __raw_readl(pmc + PMC_WAKE_STATUS);
111         reg |= ((u64)readl(pmc + PMC_WAKE2_STATUS)) << 32;
112 #endif
113         return reg;
114 }
115
116 static inline u64 read_pmc_sw_wake_status(void)
117 {
118         u64 reg;
119
120 #ifdef CONFIG_ARCH_TEGRA_2x_SOC
121         reg = readl(pmc + PMC_SW_WAKE_STATUS);
122 #else
123         reg = __raw_readl(pmc + PMC_SW_WAKE_STATUS);
124         reg |= ((u64)readl(pmc + PMC_SW_WAKE2_STATUS)) << 32;
125 #endif
126         return reg;
127 }
128
129 static inline void clear_pmc_sw_wake_status(void)
130 {
131         pmc_32kwritel(0, PMC_SW_WAKE_STATUS);
132 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
133         pmc_32kwritel(0, PMC_SW_WAKE2_STATUS);
134 #endif
135 }
136
137 int tegra_pm_irq_set_wake(int irq, int enable)
138 {
139         int wake = tegra_irq_to_wake(irq);
140
141         if (wake == -EALREADY) {
142                 /* EALREADY means wakeup event already accounted for */
143                 return 0;
144         } else if (wake == -ENOTSUPP) {
145                 /* ENOTSUPP means LP0 not supported with this wake source */
146                 WARN(enable && warn_prevent_lp0, "irq %d prevents lp0\n", irq);
147                 if (enable)
148                         tegra_prevent_lp0++;
149                 else if (!WARN_ON(tegra_prevent_lp0 == 0))
150                         tegra_prevent_lp0--;
151                 return 0;
152         } else if (wake < 0) {
153                 return -EINVAL;
154         }
155
156         if (enable)
157                 tegra_lp0_wake_enb |= 1ull << wake;
158         else
159                 tegra_lp0_wake_enb &= ~(1ull << wake);
160
161         return 0;
162 }
163
164 int tegra_pm_irq_set_wake_type(int irq, int flow_type)
165 {
166         int wake = tegra_irq_to_wake(irq);
167
168         if (wake < 0)
169                 return 0;
170
171         switch (flow_type) {
172         case IRQF_TRIGGER_FALLING:
173         case IRQF_TRIGGER_LOW:
174                 tegra_lp0_wake_level &= ~(1 << wake);
175                 tegra_lp0_wake_level_any &= ~(1 << wake);
176                 break;
177         case IRQF_TRIGGER_HIGH:
178         case IRQF_TRIGGER_RISING:
179                 tegra_lp0_wake_level |= 1 << wake;
180                 tegra_lp0_wake_level_any &= ~(1 << wake);
181                 break;
182
183         case IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING:
184                 tegra_lp0_wake_level_any |= 1 << wake;
185                 break;
186         default:
187                 return -EINVAL;
188         }
189
190         return 0;
191 }
192
193 /* translate lp0 wake sources back into irqs to catch edge triggered wakeups */
194 static void tegra_pm_irq_syscore_resume_helper(unsigned long wake_status)
195 {
196         int wake;
197         int irq;
198         struct irq_desc *desc;
199
200         for_each_set_bit(wake, &wake_status, sizeof(wake_status) * 8) {
201                 irq = tegra_wake_to_irq(wake);
202                 if (!irq) {
203                         pr_info("Resume caused by WAKE%d\n", wake);
204                         continue;
205                 }
206
207                 desc = irq_to_desc(irq);
208                 if (!desc || !desc->action || !desc->action->name) {
209                         pr_info("Resume caused by WAKE%d, irq %d\n", wake, irq);
210                         continue;
211                 }
212
213                 pr_info("Resume caused by WAKE%d, %s\n", wake,
214                         desc->action->name);
215
216                 tegra_wake_irq_count[wake]++;
217
218                 generic_handle_irq(irq);
219         }
220 }
221
222 static void tegra_pm_irq_syscore_resume(void)
223 {
224         unsigned long long wake_status = read_pmc_wake_status();
225
226         tegra_pm_irq_syscore_resume_helper((unsigned long)wake_status);
227 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
228         tegra_pm_irq_syscore_resume_helper((unsigned long)(wake_status >> 32));
229 #endif
230 }
231
232 /* set up lp0 wake sources */
233 static int tegra_pm_irq_syscore_suspend(void)
234 {
235         u32 temp;
236         u64 status;
237         u64 lvl;
238         u64 wake_level;
239         u64 wake_enb;
240
241         clear_pmc_sw_wake_status();
242
243         temp = readl(pmc + PMC_CTRL);
244         temp |= PMC_CTRL_LATCH_WAKEUPS;
245         pmc_32kwritel(temp, PMC_CTRL);
246
247         temp &= ~PMC_CTRL_LATCH_WAKEUPS;
248         pmc_32kwritel(temp, PMC_CTRL);
249
250         status = read_pmc_sw_wake_status();
251
252         lvl = read_pmc_wake_level();
253
254         /* flip the wakeup trigger for any-edge triggered pads
255          * which are currently asserting as wakeups */
256         lvl ^= status;
257         lvl &= tegra_lp0_wake_level_any;
258
259         wake_level = lvl | tegra_lp0_wake_level;
260         wake_enb = tegra_lp0_wake_enb;
261
262         if (debug_lp0) {
263                 wake_level = lvl ^ status;
264                 wake_enb = 0xffffffff;
265         }
266
267         write_pmc_wake_level(wake_level);
268
269         write_pmc_wake_mask(wake_enb);
270
271         return 0;
272 }
273
274 static struct syscore_ops tegra_pm_irq_syscore_ops = {
275         .suspend = tegra_pm_irq_syscore_suspend,
276         .resume = tegra_pm_irq_syscore_resume,
277 };
278
279 static int tegra_pm_irq_syscore_init(void)
280 {
281         register_syscore_ops(&tegra_pm_irq_syscore_ops);
282
283         return 0;
284 }
285 subsys_initcall(tegra_pm_irq_syscore_init);
286
287 #ifdef CONFIG_DEBUG_FS
288 static int tegra_pm_irq_debug_show(struct seq_file *s, void *data)
289 {
290         int wake;
291         int irq;
292         struct irq_desc *desc;
293         const char *irq_name;
294
295         seq_printf(s, "wake  irq  count  name\n");
296         seq_printf(s, "----------------------\n");
297         for (wake = 0; wake < 32; wake++) {
298                 irq = tegra_wake_to_irq(wake);
299                 if (irq < 0)
300                         continue;
301
302                 desc = irq_to_desc(irq);
303                 if (tegra_wake_irq_count[wake] == 0 && desc->action == NULL)
304                         continue;
305
306                 irq_name = (desc->action && desc->action->name) ?
307                         desc->action->name : "???";
308
309                 seq_printf(s, "%4d  %3d  %5d  %s\n",
310                         wake, irq, tegra_wake_irq_count[wake], irq_name);
311         }
312         return 0;
313 }
314
315 static int tegra_pm_irq_debug_open(struct inode *inode, struct file *file)
316 {
317         return single_open(file, tegra_pm_irq_debug_show, NULL);
318 }
319
320 static const struct file_operations tegra_pm_irq_debug_fops = {
321         .open           = tegra_pm_irq_debug_open,
322         .read           = seq_read,
323         .llseek         = seq_lseek,
324         .release        = single_release,
325 };
326
327 static int __init tegra_pm_irq_debug_init(void)
328 {
329         struct dentry *d;
330
331         d = debugfs_create_file("wake_irq", S_IRUGO, NULL, NULL,
332                 &tegra_pm_irq_debug_fops);
333         if (!d) {
334                 pr_err("Failed to create suspend_mode debug file\n");
335                 return -ENOMEM;
336         }
337
338         return 0;
339 }
340
341 late_initcall(tegra_pm_irq_debug_init);
342 #endif