aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-tegra/pm-irq.c
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
committerJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
commitfcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch)
treea57612d1888735a2ec7972891b68c1ac5ec8faea /arch/arm/mach-tegra/pm-irq.c
parent8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff)
Added missing tegra files.HEADmaster
Diffstat (limited to 'arch/arm/mach-tegra/pm-irq.c')
-rw-r--r--arch/arm/mach-tegra/pm-irq.c366
1 files changed, 366 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/pm-irq.c b/arch/arm/mach-tegra/pm-irq.c
new file mode 100644
index 00000000000..57d21361ca1
--- /dev/null
+++ b/arch/arm/mach-tegra/pm-irq.c
@@ -0,0 +1,366 @@
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#define PMC_MAX_WAKE_COUNT 64
47
48static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
49
50static u64 tegra_lp0_wake_enb;
51static u64 tegra_lp0_wake_level;
52static u64 tegra_lp0_wake_level_any;
53static int tegra_prevent_lp0;
54
55static unsigned int tegra_wake_irq_count[PMC_MAX_WAKE_COUNT];
56
57static bool debug_lp0;
58module_param(debug_lp0, bool, S_IRUGO | S_IWUSR);
59
60static bool warn_prevent_lp0;
61module_param(warn_prevent_lp0, bool, S_IRUGO | S_IWUSR);
62
63bool tegra_pm_irq_lp0_allowed(void)
64{
65 return (tegra_prevent_lp0 == 0);
66}
67
68/* ensures that sufficient time is passed for a register write to
69 * serialize into the 32KHz domain */
70static void pmc_32kwritel(u32 val, unsigned long offs)
71{
72 writel(val, pmc + offs);
73 udelay(130);
74}
75
76static inline void write_pmc_wake_mask(u64 value)
77{
78 pr_info("Wake[31-0] enable=0x%x\n", (u32)(value & 0xFFFFFFFF));
79 writel((u32)value, pmc + PMC_WAKE_MASK);
80#ifndef CONFIG_ARCH_TEGRA_2x_SOC
81 pr_info("Tegra3 wake[63-32] enable=0x%x\n", (u32)((value >> 32) &
82 0xFFFFFFFF));
83 __raw_writel((u32)(value >> 32), pmc + PMC_WAKE2_MASK);
84#endif
85}
86
87static inline u64 read_pmc_wake_level(void)
88{
89 u64 reg;
90
91#ifdef CONFIG_ARCH_TEGRA_2x_SOC
92 reg = readl(pmc + PMC_WAKE_LEVEL);
93#else
94 reg = __raw_readl(pmc + PMC_WAKE_LEVEL);
95 reg |= ((u64)readl(pmc + PMC_WAKE2_LEVEL)) << 32;
96#endif
97 return reg;
98}
99
100static inline void write_pmc_wake_level(u64 value)
101{
102 pr_info("Wake[31-0] level=0x%x\n", (u32)(value & 0xFFFFFFFF));
103 writel((u32)value, pmc + PMC_WAKE_LEVEL);
104#ifndef CONFIG_ARCH_TEGRA_2x_SOC
105 pr_info("Tegra3 wake[63-32] level=0x%x\n", (u32)((value >> 32) &
106 0xFFFFFFFF));
107 __raw_writel((u32)(value >> 32), pmc + PMC_WAKE2_LEVEL);
108#endif
109}
110
111static inline u64 read_pmc_wake_status(void)
112{
113 u64 reg;
114
115#ifdef CONFIG_ARCH_TEGRA_2x_SOC
116 reg = readl(pmc + PMC_WAKE_STATUS);
117#else
118 reg = __raw_readl(pmc + PMC_WAKE_STATUS);
119 reg |= ((u64)readl(pmc + PMC_WAKE2_STATUS)) << 32;
120#endif
121 return reg;
122}
123
124static inline u64 read_pmc_sw_wake_status(void)
125{
126 u64 reg;
127
128#ifdef CONFIG_ARCH_TEGRA_2x_SOC
129 reg = readl(pmc + PMC_SW_WAKE_STATUS);
130#else
131 reg = __raw_readl(pmc + PMC_SW_WAKE_STATUS);
132 reg |= ((u64)readl(pmc + PMC_SW_WAKE2_STATUS)) << 32;
133#endif
134 return reg;
135}
136
137static inline void clear_pmc_sw_wake_status(void)
138{
139 pmc_32kwritel(0, PMC_SW_WAKE_STATUS);
140#ifndef CONFIG_ARCH_TEGRA_2x_SOC
141 pmc_32kwritel(0, PMC_SW_WAKE2_STATUS);
142#endif
143}
144
145int tegra_pm_irq_set_wake(int irq, int enable)
146{
147 int wake = tegra_irq_to_wake(irq);
148
149 if (wake == -EALREADY) {
150 /* EALREADY means wakeup event already accounted for */
151 return 0;
152 } else if (wake == -ENOTSUPP) {
153 /* ENOTSUPP means LP0 not supported with this wake source */
154 WARN(enable && warn_prevent_lp0, "irq %d prevents lp0\n", irq);
155 if (enable)
156 tegra_prevent_lp0++;
157 else if (!WARN_ON(tegra_prevent_lp0 == 0))
158 tegra_prevent_lp0--;
159 return 0;
160 } else if (wake < 0) {
161 return -EINVAL;
162 }
163
164 if (enable) {
165 tegra_lp0_wake_enb |= 1ull << wake;
166 pr_info("Enabling wake%d\n", wake);
167 } else {
168 tegra_lp0_wake_enb &= ~(1ull << wake);
169 pr_info("Disabling wake%d\n", wake);
170 }
171
172 return 0;
173}
174
175int tegra_pm_irq_set_wake_type(int irq, int flow_type)
176{
177 int wake = tegra_irq_to_wake(irq);
178
179 if (wake < 0)
180 return 0;
181
182 switch (flow_type) {
183 case IRQF_TRIGGER_FALLING:
184 case IRQF_TRIGGER_LOW:
185 tegra_lp0_wake_level &= ~(1ull << wake);
186 tegra_lp0_wake_level_any &= ~(1ull << wake);
187 break;
188 case IRQF_TRIGGER_HIGH:
189 case IRQF_TRIGGER_RISING:
190 tegra_lp0_wake_level |= (1ull << wake);
191 tegra_lp0_wake_level_any &= ~(1ull << wake);
192 break;
193
194 case IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING:
195 tegra_lp0_wake_level_any |= (1ull << wake);
196 break;
197 default:
198 return -EINVAL;
199 }
200
201 return 0;
202}
203
204/* translate lp0 wake sources back into irqs to catch edge triggered wakeups */
205static void tegra_pm_irq_syscore_resume_helper(
206 unsigned long wake_status,
207 unsigned int index)
208{
209 int wake;
210 int irq;
211 struct irq_desc *desc;
212
213 for_each_set_bit(wake, &wake_status, sizeof(wake_status) * 8) {
214 irq = tegra_wake_to_irq(wake + 32 * index);
215 if (!irq) {
216 pr_info("Resume caused by WAKE%d\n",
217 (wake + 32 * index));
218 continue;
219 }
220
221 desc = irq_to_desc(irq);
222 if (!desc || !desc->action || !desc->action->name) {
223 pr_info("Resume caused by WAKE%d, irq %d\n",
224 (wake + 32 * index), irq);
225 continue;
226 }
227
228 pr_info("Resume caused by WAKE%d, %s\n", (wake + 32 * index),
229 desc->action->name);
230
231 tegra_wake_irq_count[wake + 32 * index]++;
232
233 generic_handle_irq(irq);
234 }
235}
236
237static void tegra_pm_irq_syscore_resume(void)
238{
239 unsigned long long wake_status = read_pmc_wake_status();
240
241 pr_info(" legacy wake status=0x%x\n", (u32)wake_status);
242 tegra_pm_irq_syscore_resume_helper((unsigned long)wake_status, 0);
243#ifndef CONFIG_ARCH_TEGRA_2x_SOC
244 pr_info(" tegra3 wake status=0x%x\n", (u32)(wake_status >> 32));
245 tegra_pm_irq_syscore_resume_helper(
246 (unsigned long)(wake_status >> 32), 1);
247#endif
248}
249
250/* set up lp0 wake sources */
251static int tegra_pm_irq_syscore_suspend(void)
252{
253 u32 temp;
254 u64 status;
255 u64 lvl;
256 u64 wake_level;
257 u64 wake_enb;
258
259 clear_pmc_sw_wake_status();
260
261 temp = readl(pmc + PMC_CTRL);
262 temp |= PMC_CTRL_LATCH_WAKEUPS;
263 pmc_32kwritel(temp, PMC_CTRL);
264
265 temp &= ~PMC_CTRL_LATCH_WAKEUPS;
266 pmc_32kwritel(temp, PMC_CTRL);
267
268 status = read_pmc_sw_wake_status();
269
270 lvl = read_pmc_wake_level();
271
272 /* flip the wakeup trigger for any-edge triggered pads
273 * which are currently asserting as wakeups */
274 lvl ^= status;
275
276 lvl &= tegra_lp0_wake_level_any;
277
278 wake_level = lvl | tegra_lp0_wake_level;
279 wake_enb = tegra_lp0_wake_enb;
280
281 if (debug_lp0) {
282 wake_level = lvl ^ status;
283 wake_enb = 0xffffffff;
284 }
285
286 /* Clear PMC Wake Status register while going to suspend */
287 temp = readl(pmc + PMC_WAKE_STATUS);
288 if (temp)
289 pmc_32kwritel(temp, PMC_WAKE_STATUS);
290
291 write_pmc_wake_level(wake_level);
292
293 write_pmc_wake_mask(wake_enb);
294
295 return 0;
296}
297
298static struct syscore_ops tegra_pm_irq_syscore_ops = {
299 .suspend = tegra_pm_irq_syscore_suspend,
300 .resume = tegra_pm_irq_syscore_resume,
301};
302
303static int tegra_pm_irq_syscore_init(void)
304{
305 register_syscore_ops(&tegra_pm_irq_syscore_ops);
306
307 return 0;
308}
309subsys_initcall(tegra_pm_irq_syscore_init);
310
311#ifdef CONFIG_DEBUG_FS
312static int tegra_pm_irq_debug_show(struct seq_file *s, void *data)
313{
314 int wake;
315 int irq;
316 struct irq_desc *desc;
317 const char *irq_name;
318
319 seq_printf(s, "wake irq count name\n");
320 seq_printf(s, "----------------------\n");
321 for (wake = 0; wake < PMC_MAX_WAKE_COUNT; wake++) {
322 irq = tegra_wake_to_irq(wake);
323 if (irq < 0)
324 continue;
325
326 desc = irq_to_desc(irq);
327 if (tegra_wake_irq_count[wake] == 0 && desc->action == NULL)
328 continue;
329
330 irq_name = (desc->action && desc->action->name) ?
331 desc->action->name : "???";
332
333 seq_printf(s, "%4d %3d %5d %s\n",
334 wake, irq, tegra_wake_irq_count[wake], irq_name);
335 }
336 return 0;
337}
338
339static int tegra_pm_irq_debug_open(struct inode *inode, struct file *file)
340{
341 return single_open(file, tegra_pm_irq_debug_show, NULL);
342}
343
344static const struct file_operations tegra_pm_irq_debug_fops = {
345 .open = tegra_pm_irq_debug_open,
346 .read = seq_read,
347 .llseek = seq_lseek,
348 .release = single_release,
349};
350
351static int __init tegra_pm_irq_debug_init(void)
352{
353 struct dentry *d;
354
355 d = debugfs_create_file("wake_irq", S_IRUGO, NULL, NULL,
356 &tegra_pm_irq_debug_fops);
357 if (!d) {
358 pr_err("Failed to create suspend_mode debug file\n");
359 return -ENOMEM;
360 }
361
362 return 0;
363}
364
365late_initcall(tegra_pm_irq_debug_init);
366#endif